惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CERT Recently Published Vulnerability Notes
U
Unit 42
T
The Blog of Author Tim Ferriss
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Securelist
L
Lohrmann on Cybersecurity
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
D
DataBreaches.Net
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
I
Intezer
P
Palo Alto Networks Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
I
InfoQ
宝玉的分享
宝玉的分享
Security Latest
Security Latest
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threatpost
Cisco Talos Blog
Cisco Talos Blog
P
Proofpoint News Feed
博客园 - 司徒正美
H
Hacker News: Front Page
Y
Y Combinator Blog
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
NISL@THU
NISL@THU
月光博客
月光博客
有赞技术团队
有赞技术团队
Cloudbric
Cloudbric
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
A
Arctic Wolf
博客园 - 【当耐特】
W
WeLiveSecurity
V
Visual Studio Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
V2EX
C
Cyber Attacks, Cyber Crime and Cyber Security
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog

博客园 - 梦想CAD控件

网页CAD图纸保存与数据持久化(BS架构CAD插件) 网页CAD中命令行驱动与参数化绘图的使用教程 在线CAD视图控制与图层管理二开实战 Web CAD图形引擎实现DWG图纸网页批注和查看 开源网页实时协同设计平台-在线编辑CAD图纸 在线CAD SDK开发包图纸转换功能使用教程(在线CAD API) 统信麒麟鸿蒙系统软件集成CAD功能(国产系统CAD控件) 网页CAD开发包结构与功能说明(网页CAD实时协同编辑) 网页CAD结合AI实现自然语言设计CAD图纸 (JS实现预览)在线CAD中的“组”功能开发 (在线CAD)html在线预览cad图纸SDK集成方法 (JS编辑DWG文件)在线CAD二开实现图纸表格提取到excel 网页版CAD与gis结合实现在线地图和CAD编辑 (html在线预览cad图纸插件)在线CAD实现形位公差标注 (WEB CAD API )网页CAD插件如何开发焊接符号 (HTML VUE在线编辑DWG)网页CAD利用AI实现多行文本功能的开发 (web cad sdk)在线CAD中配置属性的使用教程 (WEB CAD二次开发)在线CAD SDK调用内部弹框的方法 在线CAD插件通过上下文对象MxPluginContext修改UI界面的方法 国产操作系统中好用的CAD软件(统信,麒麟,信创环境安装CAD) (pdm集成CAD SDK)在线CAD绘制条形码、二维码的教程 在线CAD绘制门和窗(WEB CAD二次开发家装设计软件) (在线三维参数化建模)网页三维CAD中加载和保存STEP模型 (H5在线CAD SDK)在线CAD中线型表的使用方法 (web查看三维CAD图纸)在三维网页CAD中绘制一个窗户模型
在线CAD绘制墙体(WEB端开发室内设计软件)
梦想CAD控件 · 2024-12-24 · via 博客园 - 梦想CAD控件

前言

室内平面图中墙体是最重要的图形之一,其中砖墙、混凝土墙、钢架墙、隔墙、隔热墙等类型的墙在设计图中均有不同的表现方式,墙体的用途一般可以分为一般墙、虚墙、卫生隔断、阳台挡板、矮墙等,根据不同的需求绘制对应的墙体能够增强建筑设计的专业性和准确性。下面我们将介绍如何使用mxcad实现基础墙体功能,并展示其实践运用效果。

下述的墙体功能为一个基于mxcad开发的demo示例,因此存在无法百分百适配用户实际使用需求的情况,用户可在下述源码的基础上基于mxcad实现二次开发一次来适配实际需求。

功能开发

mxcad墙体功能的核心思想是通过继承mxcad中的自定义实体[McDbCustomEntity] ,自己实现一个独立的墙体对象,以及通过监测墙体相交的变化实现自主计算墙体绘制的一系列逻辑。如果你对 mxcad 中的自定义实体还不熟悉,点击[自定义实体开发文档链接]了解自定义实体是什么,内部存在的方法以及如何通过该实体实现自定义的功能实体。

1.封装计算墙体对应的多段线方法

在计算墙体多段线的方法中,我们需要传入墙体开始点、墙体结束点和墙体宽度,如果目标墙体存在与其他墙体相交产生拐点的情况还需要传入目标墙体生成的拐点。为了方便后续与其他墙体之间的比对和计算,我们将返回墙体的四个断点(pt1,pt2,pt3,pt4)、整体多段线(pl)、以及所在的中心直线(line),参考代码:

   // 计算多段线相关数据
   const getPolyline = (startPoint: McGePoint3d, endPoint: McGePoint3d, width: number, turnPoints?: McGePoint3d[]): any => {
       const _startPoint = startPoint.clone();
       const _endPoint = endPoint.clone();
       if (startPoint.x > endPoint.x) {
           startPoint = _endPoint;
           endPoint = _startPoint;
       }
       const pl = new McDbPolyline();
       // 计算初始墙体四个端点  
       const v = endPoint.sub(startPoint).normalize().perpVector();
       let pt1 = startPoint.clone().addvec(v.clone().mult(width / 2));
       let pt2 = startPoint.clone().addvec(v.clone().negate().mult(width / 2));
       let pt3 = endPoint.clone().addvec(v.clone().negate().mult(width / 2));
       let pt4 = endPoint.clone().addvec(v.clone().mult(width / 2));
       const pointArr = {
           pt1Arr: [],
           pt2Arr: [],
           pt3Arr: [],
           pt4Arr: []
       }
       /**
        *有拐点则计算出墙体变换后的四个端点
        */
       if (turnPoints && turnPoints.length > 0) {
           turnPoints.forEach(pt => {
               const dist1 = pt.distanceTo(startPoint);
               const dist2 = pt.distanceTo(endPoint);
               if (dist1 < dist2) {
                   // 开始端
                   const _dist1 = pt.distanceTo(pt1);
                   const _dist2 = pt.distanceTo(pt2);
                   if (_dist1 < _dist2) {
                       // pt1 = pt;
                       pointArr.pt1Arr.push(pt)
                   } else {
                       // pt2 = pt;
                       pointArr.pt2Arr.push(pt)
                   }
               } else {
                   // 结束端
                   const _dist1 = pt.distanceTo(pt3);
                   const _dist2 = pt.distanceTo(pt4);
                   if (_dist1 < _dist2) {
                       // pt3 = pt;
                       pointArr.pt3Arr.push(pt)
                   } else {
                       // pt4 = pt;
                       pointArr.pt4Arr.push(pt);
                   }
               };
           });
        // 拐点变换后新的墙体端点      
           for (let i = 0; i < 4; i++) {
               if (pointArr[`pt${i + 1}Arr`].length !== 0) {
                   let dist = null;
                   let point = new McGePoint3d();
                   switch (i + 1) {
                       case 1:
                           point = pt4;
                           break;
                       case 2:
                           point = pt3;
                           break;
                       case 3:
                           point = pt2;
                           break;
                       case 4:
                           point = pt1;
                           break;
                   }
                   pointArr[`pt${i + 1}Arr`].forEach(pt => {
                       const _dist = pt.distanceTo(point);
                       if (!dist || _dist < dist) {
                           dist = _dist;
                           switch (i + 1) {
                               case 1:
                                   pt1 = pt;
                                   break;
                               case 2:
                                   pt2 = pt;
                                   break;
                               case 3:
                                   pt3 = pt;
                                   break;
                               case 4:
                                   pt4 = pt;
                                   break;
                           }
                       }
                   })
               }
           }
       } else {
           pl.addVertexAt(pt1);
           pl.addVertexAt(pt2);
           pl.addVertexAt(pt3);
           pl.addVertexAt(pt4);
       }
       pl.isClosed = true;
       const line = new McDbLine(startPoint, endPoint);
       return { pl, line, pt1, pt2, pt3, pt4 };
   }

2.实现自定义墙体类:McDbTestWall

   // 墙体类
   class McDbTestWall extends McDbCustomEntity {
       // 定义McDbTestWall内部的点对象
       // 墙体开始点
       private startPoint: McGePoint3d = new McGePoint3d();
       // 墙体夹点移动前结束点位置
       private _oldEndPoint: McGePoint3d = new McGePoint3d();
       // 墙体夹点移动前开始点位置
       private _oldStartPoint: McGePoint3d = new McGePoint3d();
       // 墙体结束点
       private endPoint: McGePoint3d = new McGePoint3d();
       // 墙体宽
       private _wallWidth: number = 30;
       // 墙体断点
       private _breakPoints: McGePoint3d[] = [];
       // 墙体拐点
       private _turnPoints: McGePoint3d[] = [];
       // 墙体相交点
       private _insterPoints: McGePoint3d[] = [];
       // 每一个断点组的长度记录
       private breakPtsCount: number[] = [];
       // 构造函数
       constructor(imp?: any) {
           super(imp);
       }
       // 创建函数
       public create(imp: any) {
           return new McDbTestWall(imp)
       }
       // 获取类名
       public getTypeName(): string {
           return "McDbTestWall";
       }
       //设置或获取墙体宽
       public set wallWidth(val: number) {
           this._wallWidth = val;
       }
       public get wallWidth(): number {
           return this._wallWidth;
       }
       //设置或获取墙体断点
       public set breakPoints(val: McGePoint3d[]) {
           this._breakPoints = val;
       }
       public get breakPoints(): McGePoint3d[] {
           return this._breakPoints;
       }
       //设置或获取墙体拐点
       public set turnPoints(val: McGePoint3d[]) {
           this._turnPoints = val;
       }
       public get turnPoints(): McGePoint3d[] {
           return this._turnPoints;
       }
       //设置或获取墙体相交点
       public set insterPoints(val: McGePoint3d[]) {
           this._insterPoints = val;
       }
       public get insterPoints(): McGePoint3d[] {
           return this._insterPoints;
       }
       //设置或获取墙体单次相交断点数
       public set breakPtsCounts(val: number[]) {
           this.breakPtsCount = val;
       }
       public get breakPtsCounts(): number[] {
           return this.breakPtsCount;
       }
       // 获取墙体移动前开始点
       public get oldStartPoint(): McGePoint3d {
           return this._oldStartPoint;
       }
       // 获取墙体移动前结束点
       public get oldEndPoint(): McGePoint3d {
           return this._oldEndPoint;
       }
       // 读取自定义实体数据
       public dwgInFields(filter: IMcDbDwgFiler): boolean {
           this.startPoint = filter.readPoint("startPoint").val;
           this.endPoint = filter.readPoint("endPoint").val;
           this._oldEndPoint = filter.readPoint("oldEndPoint").val;
           this._oldStartPoint = filter.readPoint("oldStartPoint").val;
           this._breakPoints = filter.readPoints("breakPoint").val;
           this._insterPoints = filter.readPoints("insterPoints").val;
           this._turnPoints = filter.readPoints("turnPoint").val;
           this._wallWidth = filter.readDouble("wallWidth").val;
           const _breakPtsCount = filter.readString("breakPtsCount").val;
           if (_breakPtsCount) {
               this.breakPtsCount = _breakPtsCount.split(',').map(Number);
           }
           return true;
       }
       // 写入自定义实体数据
       public dwgOutFields(filter: IMcDbDwgFiler): boolean {
           filter.writePoint("endPoint", this.endPoint);
           filter.writePoint("startPoint", this.startPoint);
           filter.writePoint("oldStartPoint", this._oldStartPoint);
           filter.writePoint("oldEndPoint", this._oldEndPoint);
           filter.writePoints("breakPoint", this._breakPoints);
           filter.writePoints("insterPoints", this._insterPoints);
           filter.writePoints("turnPoint", this._turnPoints);
           filter.writeDouble("wallWidth", this._wallWidth);
           if (this.breakPtsCount.length > 0) {
               filter.writeString("breakPtsCount", this.breakPtsCount.join(','));
           }
           return true;
       }
   
       // 移动自定义对象的夹点
       public moveGripPointsAt(iIndex: number, dXOffset: number, dYOffset: number, dZOffset: number) {
           this.assertWrite();
           if (iIndex === 0) {
               this._oldStartPoint = this.startPoint.clone();
               this.startPoint.x += dXOffset;
               this.startPoint.y += dYOffset;
               this.startPoint.z += dZOffset;
           } else if (iIndex === 1) {
               this._oldEndPoint = this.endPoint.clone();
               this.endPoint.x += dXOffset;
               this.endPoint.y += dYOffset;
               this.endPoint.z += dZOffset;
           };
       };
       // 获取自定义对象的夹点
       public getGripPoints(): McGePoint3dArray {
           let ret = new McGePoint3dArray()
           ret.append(this.startPoint);
           ret.append(this.endPoint);
           return ret;
       };
       // 绘制实体
       public worldDraw(draw: MxCADWorldDraw): void {
           const { pl, pt1, pt2, pt3, pt4 } = getPolyline(this.startPoint.clone(), this.endPoint.clone(), this._wallWidth, this._turnPoints);
           const dol = 0.00001;
           if (this.turnPoints.length > 0 || this.breakPoints.length > 0) {
               // 有拐点,有断点
               const lineArr: McDbLine[] = [];
               const line1 = new McDbLine(pt1, pt4);
               const line2 = new McDbLine(pt2, pt3);
               lineArr.push(line2, line1)
               const dist1 = pt1.distanceTo(pt2);
               const dist2 = pt4.distanceTo(pt3);
               if (dist1 - this._wallWidth < 0.00001) {
                   const line3 = new McDbLine(pt2, pt1);
                   if (this._turnPoints.length > 0) {
                       let r = this._turnPoints.filter(pt => pt2.distanceTo(pt) < dol || pt1.distanceTo(pt) < dol);
                       if (r.length === 0) lineArr.push(line3)
                   } else {
                       lineArr.push(line3);
                   }
               }
               if (dist2 - this._wallWidth < 0.00001) {
                   const line4 = new McDbLine(pt3, pt4);
                   if (this._turnPoints.length > 0) {
                       let r = this._turnPoints.filter(pt => pt3.distanceTo(pt) < dol || pt4.distanceTo(pt) < dol);
                       if (r.length === 0) lineArr.push(line4)
                   } else {
                       lineArr.push(line4)
                   }
               };
               if (this.breakPoints.length > 0) {
                   /**
                    * 有断点
                    * 找出每一个断点所在的直线
                    * 以每一组断点去切割直线,再绘制切割好的直线
                    */
                   let count = 0;
                   const breakPlArr: McDbPolyline[] = []; // 存储每一组断点多段线
                   const breakPts = [];
                   this.breakPtsCount.forEach((item) => {
                       count += item;
                       const pl = new McDbPolyline()
                       if (count > 0) {
                           // 一组断点
                           const breakPoint = this.breakPoints.filter((pt, i) => i < count && i >= count - item);
                           breakPoint.forEach(pt => {
                               pl.addVertexAt(pt);
                           })
                           breakPts.push(breakPoint)
                       };
                       breakPlArr.push(pl);
                   });
                   lineArr.forEach((l) => {
                       // 直线上的断点集合
                       const plArr: McDbPolyline[] = []; // 直线上的断点连线
                       const r: McGePoint3d[] = []; // 直线上的断点集合
                       const _r: McGePoint3d[] = []; // 直线上的单断点集合
                       breakPts.forEach((item) => {
                           const points = item.filter(pt => {
                               const point = l.getClosestPointTo(pt, false).val;
                               const dist = point.distanceTo(pt);
                               return dist < dol
                           });
                           if (points.length > 0) {
                               r.push(...points)
                               if (points.length % 2 === 0) {
                                   points.forEach((pt, index) => {
                                       if (index % 2 == 0) {
                                           const pl = new McDbPolyline();
                                           pl.addVertexAt(pt);
                                           pl.addVertexAt(points[index + 1]);
                                           plArr.push(pl);
                                       }
                                   });
                               } else {
                                   _r.push(...points)
                               }
                           }
                       })
                       if (r.length > 0) {
                           // 直线上含有断点
                           l.splitCurves(r).forEach((obj) => {
                               const _line = obj.clone() as McDbLine;
                               const midPt = _line.startPoint.clone().addvec(_line.endPoint.sub(_line.startPoint).mult(1 / 2))
                               const res1 = r.filter(pt => _line.startPoint.distanceTo(pt) < dol);
                               const res2 = r.filter(pt => _line.endPoint.distanceTo(pt) < dol);
                               // 起始点只有一个是断点
                               if (res1.length === 0 || res2.length === 0) {
                                   if (_r.length === 0) {
                                       // 无单断点直接绘制
                                       draw.drawEntity(_line)
                                   } else {
                                       // 筛选出在切断后直线的单断点
                                       const singlePts = _r.filter(pt => _line.getDistAtPoint(pt).ret);//切割后线段上的单断点
                                       if (singlePts.length === 0) {
                                           // 若单断点不在这条直线上,则直接绘制
                                           draw.drawEntity(_line)
                                       } else {
                                           // 若单断点在这条直线上,则判断是否与对应的交点同向
                                           singlePts.forEach(pt => {
                                               if (breakPlArr.length > 0) {
                                                   const _plNum = breakPlArr.filter((pl, index) => {
                                                       if (pl.numVerts() === 2) {
                                                           return this.countLineToPl(pl, index, pt, midPt);
                                                       } else {
                                                           const num = pl.numVerts();
                                                           let flag = false;
                                                           for (let i = 0; i < num; i++) {
                                                               if (i % 2 == 0) {
                                                                   const _pl = new McDbPolyline();
                                                                   _pl.addVertexAt(pl.getPointAt(i).val);
                                                                   _pl.addVertexAt(pl.getPointAt(i + 1).val);
                                                                   _pl.trueColor = new McCmColor(255, 0, 0);
                                                                   const r = this.countLineToPl(_pl, index, pt, midPt)
                                                                   if (r) flag = true;
                                                               }
                                                           };
                                                           return flag
                                                       }
   
                                                   });
                                                   if (_plNum.length === singlePts.length) {
                                                       draw.drawEntity(_line)
                                                   }
                                               }
                                           })
                                       }
   
                                   }
                               }
                               // 起始点都是断点
                               if (res1.length !== 0 && res2.length !== 0) {
                                   const _r1 = plArr.filter((pl) => {
                                       const pt = pl.getClosestPointTo(midPt, false).val;
                                       return pt.distanceTo(midPt) < dol
                                   });
                                   if (_r1.length === 0) {
                                       draw.drawEntity(_line)
                                   }
                               }
                           })
                       } else {
                           // 线段上无断点
                           const length = l.getLength().val;
                           if (Math.abs(length - this._wallWidth) < dol) {
                               const midPt = l.getPointAtDist(l.getLength().val / 2).val;
                               const distArr = this.insterPoints.map(pt => pt.distanceTo(midPt));
                               const index = distArr.findIndex(element => element === Math.min(...distArr));
                               const pl = breakPlArr[index];
                               const insterPt = this._insterPoints[index];
                               const p = l.getClosestPointTo(insterPt, true).val;
                               if (p.distanceTo(insterPt) > dol) {
                                   const closePt = pl.getClosestPointTo(insterPt, true).val;
                                   const insterWallWidth = closePt.distanceTo(insterPt);
                                   const insterPtToLine = l.getClosestPointTo(insterPt, true).val.distanceTo(insterPt);
                                   if (insterPtToLine > insterWallWidth) {
                                       draw.drawEntity(l)
                                   } else {
                                       const midVec = midPt.sub(insterPt);
                                       const num = pl.numVerts();
                                       if (num === 2) {
                                           const vec = pl.getPointAt(0).val.sub(pl.getPointAt(1).val);
                                           const angle = Number((midVec.angleTo1(vec) * (180 / Math.PI)).toFixed(4));
                                           if (angle === 0 || angle === 180) {
                                               draw.drawEntity(l)
                                           }
                                       } else {
                                           let count = 0;
                                           for (let i = 0; i < num; i++) {
                                               if (i % 2 == 0) {
                                                   const _pl = new McDbPolyline();
                                                   _pl.addVertexAt(pl.getPointAt(i).val);
                                                   _pl.addVertexAt(pl.getPointAt(i + 1).val);
                                                   _pl.trueColor = new McCmColor(255, 0, 0);
                                                   const vec = _pl.getPointAt(0).val.sub(_pl.getPointAt(1).val);
                                                   const angle = Number((midVec.angleTo1(vec) * (180 / Math.PI)).toFixed(4));
                                                   if (angle === 0 || angle === 180) {
                                                       count += 1;
                                                   }
                                               }
                                           }
                                           if (count === num / 2) draw.drawEntity(l);
                                       }
                                   }
                               }
   
                           } else {
                               draw.drawEntity(l)
                           }
                       }
                   });
               } else {
                   // 无断点,全是拐点
                   lineArr.forEach((l, index) => {
                       if (index < 2) {
                           draw.drawEntity(l)
                       } else {
                           const length = l.getLength().val;
                           const r = this._turnPoints.filter(pt => pt.distanceTo(l.startPoint) < dol || pt.distanceTo(l.endPoint) < dol)
                           if (Math.abs(length - this._wallWidth) < dol && r.length === 0) {
                               draw.drawEntity(l)
                           }
                       }
                   })
               }
           }
           if (!this.breakPoints.length && !this.turnPoints.length) {
               draw.drawEntity(pl);
           }
           const line = new McDbLine(this.startPoint, this.endPoint)
           draw.drawOsnapEntity(line);
       }
       private countLineToPl(pl: McDbPolyline, index: number, pt: McGePoint3d, midPt: McGePoint3d): Boolean {
           const dol = 0.00001;
           const dist1 = pl.getPointAt(0).val.distanceTo(pt);
           const dist2 = pl.getPointAt(1).val.distanceTo(pt);
           const insterPt = this._insterPoints[index];
           if (dist1 < dol || dist2 < dol) {
               const _closePt = pl.getClosestPointTo(insterPt, true).val;
               const v = insterPt.sub(_closePt);
               const _v = midPt.sub(_closePt);
               let angle1 = v.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
               let angle2 = _v.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
               const angle = Math.abs(angle1 - angle2);
               if (Number((angle * (180 / Math.PI)).toFixed(4)) >= 90 && Number((angle * (180 / Math.PI)).toFixed(4)) <= 270) {
                   return true
               } else {
                   return false
               }
           } else {
               return false
           }
       }
       // 设置墙体起点
       public setStartPoint(pt: McGePoint3d) {
           this.startPoint = pt.clone()
           this._oldStartPoint = pt.clone()
       }
       // 获取墙体起点
       public getStartPoint(): McGePoint3d {
           return this.startPoint;
       }
       // 设置墙体结束点
       public setEndPoint(pt: McGePoint3d) {
           this.endPoint = pt.clone()
           this._oldEndPoint = pt.clone()
       }
       // 获取墙体结束点
       public getEndPoint(): McGePoint3d {
           return this.endPoint.clone();
       }
       // 增加墙体断点
       public addBreakPoints(pts: McGePoint3d[]) {
           this.breakPoints.push(...pts);
       }
       // 增加墙体交点
       public addInsterPoints(pts: McGePoint3d[]) {
           this._insterPoints.push(...pts);
       }
       // 增加墙体单次相交的断点数
       public addBreakPtsCount(val: number[]) {
           if (val.length > 0) {
               this.breakPtsCount.push(...val);
           }
       }
   };

3.计算墙体相交后的断点和拐点。

计算与目标墙体相交的墙体,参考代码:

     //相交墙体集合
     const intersectingWalls: McObjectId[] = [];
     /**
      * startPoint:墙体开始点
      * endPoint:墙体结束点
      * wallWidth:墙体宽度
      */
     const { pt1, pt3, line } = getPolyline(startPoint, endPoint, wallWidth);
     const length = line.getLength().val;
     // 设置过滤器,过滤出自定义实体
     let filter = new MxCADResbuf();
     filter.AddString("McDbCustomEntity", 5020);
     const ss = new MxCADSelectionSet();
     ss.crossingSelect(pt1.x - length, pt1.y - length, pt3.x + length, pt3.y + length, filter);
     // 与其他墙体相交
     if (ss.count() !== 0) {
         ss.forEach(id => {
             const ent = id.getMcDbEntity();
             if ((ent as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                 intersectingWalls.push(id);
             }
         });
     }

根据相交墙体信息获取墙体断点与拐点,参考代码:

     // 处理相交墙体得到断点与拐点
     /**
      * this.startPoint:墙体开始点
      * this.endPoint:墙体结束点
      * this.wallWidth:墙体宽度
      * this.pointArr:断点集合
      * this.breakPtsCount:单次断点数集合
      * this.turnPointArr:拐点集合
      * this.insterPointArr:交点集合
      * this.dol:精度
      */
     function dealingWithWalls(wallObjIds: McObjectId[]) {
         const { pl, pt1, pt2, pt3, pt4, line } = getPolyline(this.startPoint, this.endPoint, this.wallWidth);
         if (wallObjIds.length != 0) {
             // 与新墙体有交点的墙体集合
             const wallArr = [];
             // 与其他墙体相交
             wallObjIds.forEach(id => {
                 const entity = id.getMcDbEntity();
                 if ((entity as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                     const wall = entity.clone() as McDbTestWall
                     const { pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line } = getPolyline(wall.getStartPoint(), wall.getEndPoint(), wall.wallWidth);
                     const _pointArr: McGePoint3d[] = [];
                     _pl.IntersectWith(pl, McDb.Intersect.kOnBothOperands).forEach(item => _pointArr.push(item));
                     if (_pointArr.length > 0) {
                         wallArr.push({ id, wall, pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line, pointArr: _pointArr })
                     }
                 }
             });
             if (wallArr.length != 0) {
                 wallArr.forEach(item => {
                     const { id, wall, pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line, pointArr: _pointArr } = item;
                     /**
                          * 根据交点位置判断是否是拐点
                          * 中心相交的交点与比对中心线端点距离小于1/2墙宽的都是拐点
                          */
                     const insterPointArr = (line as McDbLine).IntersectWith(_line as McDbLine, McDb.Intersect.kExtendBoth);
                     if (insterPointArr.length() > 0) {
                         const insterPt = insterPointArr.at(0);
                         this.insterPointArr.push(insterPt);
                         const dist1 = insterPt.distanceTo(_line.startPoint);
                         const dist2 = insterPt.distanceTo(_line.endPoint);
                         const dist3 = insterPt.distanceTo(line.startPoint);
                         const dist4 = insterPt.distanceTo(line.endPoint);
                         let vec, _vec;
                         dist3 < dist4 ? vec = (line as McDbLine).endPoint.sub(insterPt).normalize() :
                         vec = (line as McDbLine).startPoint.sub(insterPt).normalize();
                         dist1 < dist2 ? _vec = (_line as McDbLine).endPoint.sub(insterPt).normalize() :
                         _vec = (_line as McDbLine).startPoint.sub(insterPt).normalize();
                         const angle1 = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                         const angle2 = _vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                         if ((dist1 <= wall.wallWidth / 2 || dist2 <= wall.wallWidth / 2) &&
                             (dist3 <= this.wallWidth / 2 || dist4 <= this.wallWidth / 2)
                            ) {
                             // 绘制拐角
                             const turnArr = []
                             const line1 = new McDbLine(pt1, pt4);
                             const line2 = new McDbLine(pt2, pt3);
                             const _line1 = new McDbLine(_pt1, _pt4);
                             const _line2 = new McDbLine(_pt2, _pt3);
                             let line1ClosePt, line2ClosePt;
                             pt1.distanceTo(insterPt) < pt4.distanceTo(insterPt) ? line1ClosePt = pt1.clone() : line1ClosePt = pt4.clone();
                             pt2.distanceTo(insterPt) < pt3.distanceTo(insterPt) ? line2ClosePt = pt2.clone() : line2ClosePt = pt3.clone();
                             const lineInsterPts1 = line1.IntersectWith(_line1, McDb.Intersect.kExtendBoth)
                             const lineInsterPts2 = line1.IntersectWith(_line2, McDb.Intersect.kExtendBoth)
                             const lineInsterPts3 = line2.IntersectWith(_line1, McDb.Intersect.kExtendBoth)
                             const lineInsterPts4 = line2.IntersectWith(_line2, McDb.Intersect.kExtendBoth)
                             const _point1 = lineInsterPts1.length() > 0 ? lineInsterPts1.at(0) : line1ClosePt;
                             const _point2 = lineInsterPts2.length() > 0 ? lineInsterPts2.at(0) : line1ClosePt;
                             const _point3 = lineInsterPts3.length() > 0 ? lineInsterPts3.at(0) : line2ClosePt;
                             const _point4 = lineInsterPts4.length() > 0 ? lineInsterPts4.at(0) : line2ClosePt;
                             const inflectioPoints = [_point1, _point2, _point3, _point4];
                             const r = inflectioPoints.filter((pt) => {
                                 const v = pt.sub(insterPt);
                                 const angle3 = v.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                                 return this.isAngleBetween(angle3, angle1, angle2)
                             })
                             if (r.length) {
                                 turnArr.push(r[0])
                                 const point = r[0];
                                 const _r = inflectioPoints.filter((pt) => {
                                     const v = pt.sub(insterPt);
                                     const _v = point.sub(insterPt)
                                     const angle = v.angleTo2(_v, McGeVector3d.kNegateZAxis);
                                     return Math.abs(angle - Math.PI) < this.dol
                                 })
                                 if (_r.length) turnArr.push(_r[0])
                             }
                             this.turnPointArr.push(...turnArr);
                             this.breakPtsCount.push(0);
                             const _wall = wall.clone() as McDbTestWall;
                             _wall.turnPoints = [..._wall.turnPoints, ...turnArr];
                             _wall.addInsterPoints([insterPt]);
                             _wall.addBreakPtsCount([0]);
                             MxCpp.getCurrentMxCAD().drawEntity(_wall);
                             id.erase();
                         } else {
                             // 没有拐角直接根据断点绘制
                             this.pointArr.push(..._pointArr);
                             this.breakPtsCount.push(_pointArr.length);
                             const _wall = wall.clone() as McDbTestWall;
                             _wall.addBreakPoints(_pointArr);
                             _wall.addInsterPoints([insterPt]);
                             _wall.addBreakPtsCount([_pointArr.length]);
                             MxCpp.getCurrentMxCAD().drawEntity(_wall);
                             id.erase();
                         }
                     }
     
                 });
             }
         }
     }
     // 计算角度方向是否在指定角度范围内
     function isAngleBetween(angleRad: number, startRad: number, endRad: number) {
             let minRad, maxRad
             if (startRad - endRad > 0) {
                 minRad = endRad;
                 maxRad = startRad;
             } else {
                 minRad = startRad;
                 maxRad = endRad;
             }
             if (maxRad - minRad > Math.PI) {
                 return angleRad <= minRad || angleRad >= maxRad;
             } else {
                 return angleRad >= minRad && angleRad <= maxRad;
             }
         }

4.整合绘制墙体方法:MxdrawWalls

// 绘制墙体类
   class MxdrawWalls {
       // 墙体开始点
       private startPoint: McGePoint3d = new McGePoint3d();
       // 墙体结束点
       private endPoint: McGePoint3d = new McGePoint3d();
       // 墙体宽度
       private wallWidth: number = 10;
       // 断点集合
       private pointArr: McGePoint3d[] = [];
       // 拐点集合
       private turnPointArr: McGePoint3d[] = [];
       // 交点集合
       private insterPointArr: McGePoint3d[] = [];
       // 单次断点数集合
       private breakPtsCount: number[] = [];
       // 精度
       private dol = 0.00001;
       // 获取相交墙体
       public getIntersectingWalls(startPoint: McGePoint3d, endPoint: McGePoint3d, wallWidth: number): McObjectId[] {
           this.startPoint = startPoint.clone();
           this.endPoint = endPoint.clone();
           this.wallWidth = wallWidth;
           const intersectingWalls: McObjectId[] = [];
           const { pt1, pt3, line } = getPolyline(startPoint, endPoint, wallWidth);
           const length = line.getLength().val;
           // 设置过滤器,过滤出自定义实体
           let filter = new MxCADResbuf();
           filter.AddString("McDbCustomEntity", 5020);
           const ss = new MxCADSelectionSet();
           ss.crossingSelect(pt1.x - length, pt1.y - length, pt3.x + length, pt3.y + length, filter);
           // 与其他墙体相交
           if (ss.count() !== 0) {
               ss.forEach(id => {
                   const ent = id.getMcDbEntity();
                   if ((ent as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                       intersectingWalls.push(id);
                   }
               });
           }
           return intersectingWalls;
       }
       // 初始绘制墙体
       public drawWall(startPoint: McGePoint3d, endPoint: McGePoint3d, wallWidth: number): McObjectId {
           const wallObjIds = this.getIntersectingWalls(startPoint, endPoint, wallWidth);
           this.dealingWithWalls(wallObjIds);
           const wallId = this.draw();
           MxCpp.getCurrentMxCAD().updateDisplay();
           return wallId
       }
       private isAngleBetween(angleRad: number, startRad: number, endRad: number) {
           let minRad, maxRad
           if (startRad - endRad > 0) {
               minRad = endRad;
               maxRad = startRad;
           } else {
               minRad = startRad;
               maxRad = endRad;
           }
           if (maxRad - minRad > Math.PI) {
               return angleRad <= minRad || angleRad >= maxRad;
           } else {
               return angleRad >= minRad && angleRad <= maxRad;
           }
       }
       // 处理相交墙体得到断点与拐点
       private dealingWithWalls(wallObjIds: McObjectId[]) {
           const { pl, pt1, pt2, pt3, pt4, line } = getPolyline(this.startPoint, this.endPoint, this.wallWidth);
           if (wallObjIds.length != 0) {
               // 与新墙体有交点的墙体集合
               const wallArr = [];
               // 与其他墙体相交
               wallObjIds.forEach(id => {
                   const entity = id.getMcDbEntity();
                   if ((entity as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                       const wall = entity.clone() as McDbTestWall
                       const { pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line } = getPolyline(wall.getStartPoint(), wall.getEndPoint(), wall.wallWidth);
                       const _pointArr: McGePoint3d[] = [];
                       _pl.IntersectWith(pl, McDb.Intersect.kOnBothOperands).forEach(item => _pointArr.push(item));
                       if (_pointArr.length > 0) {
                           wallArr.push({ id, wall, pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line, pointArr: _pointArr })
                       }
                   }
               });
               if (wallArr.length != 0) {
                   wallArr.forEach(item => {
                       const { id, wall, pl: _pl, pt1: _pt1, pt2: _pt2, pt3: _pt3, pt4: _pt4, line: _line, pointArr: _pointArr } = item;
                       /**
                        * 根据交点位置判断是否是拐点
                        * 中心相交的交点与比对中心线端点距离小于1/2墙宽的都是拐点
                        */
                       const insterPointArr = (line as McDbLine).IntersectWith(_line as McDbLine, McDb.Intersect.kExtendBoth);
                       if (insterPointArr.length() > 0) {
                           const insterPt = insterPointArr.at(0);
                           this.insterPointArr.push(insterPt);
                           const dist1 = insterPt.distanceTo(_line.startPoint);
                           const dist2 = insterPt.distanceTo(_line.endPoint);
                           const dist3 = insterPt.distanceTo(line.startPoint);
                           const dist4 = insterPt.distanceTo(line.endPoint);
                           let vec, _vec;
                           dist3 < dist4 ? vec = (line as McDbLine).endPoint.sub(insterPt).normalize() :
                               vec = (line as McDbLine).startPoint.sub(insterPt).normalize();
                           dist1 < dist2 ? _vec = (_line as McDbLine).endPoint.sub(insterPt).normalize() :
                               _vec = (_line as McDbLine).startPoint.sub(insterPt).normalize();
                           const angle1 = vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                           const angle2 = _vec.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                           if ((dist1 <= wall.wallWidth / 2 || dist2 <= wall.wallWidth / 2) &&
                               (dist3 <= this.wallWidth / 2 || dist4 <= this.wallWidth / 2)
                           ) {
                               // 绘制拐角
                               const turnArr = []
                               const line1 = new McDbLine(pt1, pt4);
                               const line2 = new McDbLine(pt2, pt3);
                               const _line1 = new McDbLine(_pt1, _pt4);
                               const _line2 = new McDbLine(_pt2, _pt3);
                               let line1ClosePt, line2ClosePt;
                               pt1.distanceTo(insterPt) < pt4.distanceTo(insterPt) ? line1ClosePt = pt1.clone() : line1ClosePt = pt4.clone();
                               pt2.distanceTo(insterPt) < pt3.distanceTo(insterPt) ? line2ClosePt = pt2.clone() : line2ClosePt = pt3.clone();
                               const lineInsterPts1 = line1.IntersectWith(_line1, McDb.Intersect.kExtendBoth)
                               const lineInsterPts2 = line1.IntersectWith(_line2, McDb.Intersect.kExtendBoth)
                               const lineInsterPts3 = line2.IntersectWith(_line1, McDb.Intersect.kExtendBoth)
                               const lineInsterPts4 = line2.IntersectWith(_line2, McDb.Intersect.kExtendBoth)
                               const _point1 = lineInsterPts1.length() > 0 ? lineInsterPts1.at(0) : line1ClosePt;
                               const _point2 = lineInsterPts2.length() > 0 ? lineInsterPts2.at(0) : line1ClosePt;
                               const _point3 = lineInsterPts3.length() > 0 ? lineInsterPts3.at(0) : line2ClosePt;
                               const _point4 = lineInsterPts4.length() > 0 ? lineInsterPts4.at(0) : line2ClosePt;
                               const inflectioPoints = [_point1, _point2, _point3, _point4];
                               const r = inflectioPoints.filter((pt) => {
                                   const v = pt.sub(insterPt);
                                   const angle3 = v.angleTo2(McGeVector3d.kXAxis, McGeVector3d.kNegateZAxis);
                                   return this.isAngleBetween(angle3, angle1, angle2)
                               })
                               if (r.length) {
                                   turnArr.push(r[0])
                                   const point = r[0];
                                   const _r = inflectioPoints.filter((pt) => {
                                       const v = pt.sub(insterPt);
                                       const _v = point.sub(insterPt)
                                       const angle = v.angleTo2(_v, McGeVector3d.kNegateZAxis);
                                       return Math.abs(angle - Math.PI) < this.dol
                                   })
                                   if (_r.length) turnArr.push(_r[0])
                               }
                               this.turnPointArr.push(...turnArr);
                               this.breakPtsCount.push(0);
                               const _wall = wall.clone() as McDbTestWall;
                               _wall.turnPoints = [..._wall.turnPoints, ...turnArr];
                               _wall.addInsterPoints([insterPt]);
                               _wall.addBreakPtsCount([0]);
                               MxCpp.getCurrentMxCAD().drawEntity(_wall);
                               id.erase();
                           } else {
                               // 没有拐角直接根据断点绘制
                               this.pointArr.push(..._pointArr);
                               this.breakPtsCount.push(_pointArr.length);
                               const _wall = wall.clone() as McDbTestWall;
                               _wall.addBreakPoints(_pointArr);
                               _wall.addInsterPoints([insterPt]);
                               _wall.addBreakPtsCount([_pointArr.length]);
                               MxCpp.getCurrentMxCAD().drawEntity(_wall);
                               id.erase();
                           }
                       }
   
                   });
               }
           }
       }
       // 绘制墙体
       private draw(): McObjectId {
           const mxcad = MxCpp.getCurrentMxCAD();
           const wall = new McDbTestWall();
           wall.setEndPoint(this.endPoint);
           wall.setStartPoint(this.startPoint);
           wall.wallWidth = this.wallWidth;
           if (this.pointArr && this.pointArr.length) {
               wall.addBreakPoints(this.pointArr)
           }
           wall.turnPoints = this.turnPointArr ? this.turnPointArr : [];
           if (this.insterPointArr && this.insterPointArr.length) {
               wall.addInsterPoints(this.insterPointArr)
           }
           if (this.breakPtsCount && this.breakPtsCount.length) {
               wall.addBreakPtsCount(this.breakPtsCount)
           }
           return mxcad.drawEntity(wall);
       }
      // 更新墙体
       public updateWall(wallId: McObjectId, isRedraw: Boolean) {
           const wall = wallId.getMcDbEntity() as McDbTestWall;
           if (wall.objectName === "McDbCustomEntity" && (wall as McDbCustomEntity).getTypeName() === "McDbTestWall") {
               const wallClone = wall.clone() as McDbTestWall;
               // 先修改移动前相交墙体里的断点和拐点
               const oldWallObjIds = this.getIntersectingWalls(wall.oldStartPoint, wall.oldEndPoint, wall.wallWidth);
               // 更新与墙体相交的墙
               oldWallObjIds.forEach(id => {
                   const _wall = (id.getMcDbEntity()) as McDbTestWall;
                   _wall.breakPoints = _wall.breakPoints.filter(pt => {
                       const r = wallClone.breakPoints.filter(point => point.distanceTo(pt) < this.dol);
                       return r.length === 0;
                   });
                   _wall.turnPoints = _wall.turnPoints.filter(pt => {
                       const r = wallClone.turnPoints.filter(point => point.distanceTo(pt) < this.dol);
                       return r.length === 0;
                   });
                   _wall.insterPoints = _wall.insterPoints.filter((pt, index) => {
                       const r = wallClone.insterPoints.filter(point => point.distanceTo(pt) < this.dol);
                       if (r.length === 0) {
                           return true
                       } else {
                           _wall.breakPtsCounts.splice(index, 1);
                       }
                   });
                   _wall.assertObjectModification(true);
               });
               // 是否重绘墙体,重新计算墙体点位
               if (isRedraw) {
                   wallId.erase();
                   setTimeout(() => {
                       this.drawWall(wall.getStartPoint(), wall.getEndPoint(), wall.wallWidth);
                   }, 0)
               };
           } else {
               return
           }
       }

5.调用 MxdrawWalls 绘制墙体

// 绘制墙体
   async function drawWall() {
       const getWallWidth = new MxCADUiPrDist();
       getWallWidth.setMessage(`\n${t('请设置墙体宽度')}:`);
       let wallWidth = await getWallWidth.go();
       if (!wallWidth) wallWidth = 30;
       while (true) {
           const getPoint1 = new MxCADUiPrPoint();
           getPoint1.setMessage(`\n${t('请设置墙体起点')}:`);
           const pt1 = await getPoint1.go();
           if (!pt1) break;
   
           const getDist = new MxCADUiPrDist();
           getDist.setBasePt(pt1);
           getDist.setMessage(`\n${t('请设置墙体长度')}:`);
           let pt2 = new McGePoint3d();
           getDist.setUserDraw((pt, pw) => {
               const v = pt1.sub(pt).normalize().perpVector().mult(wallWidth / 2);
               const line1 = new McDbLine(pt1.clone().addvec(v), pt.clone().addvec(v));
               pw.setColor(0x0000FF);
               pw.drawMcDbEntity(line1);
   
               const line2 = new McDbLine(pt1.clone().addvec(v.clone().negate()), pt.clone().addvec(v.clone().negate()));
               pw.setColor(0xFF0000);
               pw.drawMcDbEntity(line2);
               pt2 = pt.clone();
           });
           const dist = await getDist.go();
           if (!dist) break;
           const v = pt2.sub(pt1).normalize().mult(dist);
           const endPt = pt1.clone().addvec(v)
           const wall = new MxdrawWalls()
           wall.drawWall(pt1, endPt, wallWidth);
       }
   }

6.夹点编辑、实体删除情况处理

调用夹点编辑监听事件,监听墙体变化,当墙体位置发生改变则触发墙体更新方法,参考代码:

const mxcad: McObject = MxCpp.getCurrentMxCAD();
     // 监听wall夹点变化
     mxcad.mxdraw.on("objectGripEdit", (grips) => {
         grips.forEach((grip) => {
             const id = new McObjectId(grip.id, grip.type === "mxcad" ? McObjectIdType.kMxCAD : McObjectIdType.kMxDraw);
             if (id.isErase()) return;
             const wall = id.getMcDbEntity() as McDbTestWall;
             if(!wall) return;
             
             if (wall.objectName === "McDbCustomEntity" && (wall as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                 // 更新绘制wall
                 const newWall = new MxdrawWalls();
                 newWall.updateWall(id, true);
                 mxcad.clearMxCurrentSelect();
             };
         })
     })

调用实体选择监听事件,监听实体选择,若监听到选择的墙体被删除则触法墙体更新方法,参考代码:

     // 监听mxcad选择,若wall被删除则触发更新
     const oldSelectIds: McObjectId[] = [];
     mxcad.on("selectChange", (ids: McObjectId[]) => {
         if (ids.length > 0) {
             ids.forEach(id => {
                 const entity = id.getMcDbEntity();
                 if (entity && entity.objectName === "McDbCustomEntity" && (entity as McDbCustomEntity).getTypeName() === "McDbTestWall") {
                     oldSelectIds.push(id)
                 }
             })
         } else {
             setTimeout(()=>{
                 oldSelectIds.forEach(id => {
                     if (id.isErase()) {
                         const newWall = new MxdrawWalls();
                         newWall.updateWall(id, false);
                     }
                 });
                 oldSelectIds.length = 0;
             },0)
         }
     })

功能实践

在线示例demo地址:https://demo.mxdraw3d.com:3000/mxcad/

墙体绘制效果展示: