diff --git "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" index 6bd4b8236c7a82fea55c74f6764f8ecbbd728afa..34e5824a2438061b578d4a858a71b5b8c51b41a6 100644 --- "a/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" +++ "b/src/CAD/IFox.CAD.Shared/ExtensionMethod/\346\226\260\345\273\272\345\241\253\345\205\205/HatchEx.cs" @@ -1,7 +1,5 @@ -namespace IFoxCAD.Cad; -/// -/// 充填扩展类 -/// +namespace IFoxCAD.Cad; + public static class HatchEx { /// @@ -14,4 +12,303 @@ public static void ForEach(this Hatch hatch, Action action) for (int i = 0; i < hatch.NumberOfLoops; i++) action.Invoke(hatch.GetLoopAt(i)); } + + /// + /// 提取已存在的关联边界(一个边界环里所有的对象 id 组成一个 ObjectIdCollection) + /// + /// + /// + /// + public static List? GetAssociatedBoundaryIds(this Hatch hatch) + { + if (!hatch.Id.IsOk()) + throw new ArgumentException("填充未加入或不存在于数据库"); + + if (!hatch.Associative) + return null; + + var listObjIdColl = new List(); + + for (var i = 0; i < hatch.NumberOfLoops; i++) + { + var assIds = hatch.GetAssociatedObjectIdsAt(i); + if (assIds != null) + { + var objIds = new ObjectIdCollection(); + foreach (ObjectId id in assIds) + { + if (id.IsOk()) + objIds.Add(id); + } + + if (objIds.Count == 0) + { + throw new ArgumentException("关联的填充边界被删除后没有清理反应器,请调用:" + + "\n hatch.RemoveAssociatedObjectIds()" + + "\n hatch.Associative = false"); + } + + listObjIdColl.Add(objIds); + } + } + + return listObjIdColl; + } + + /// + /// 创建边界(仅创建于内存中,未加入数据库) + /// + /// + /// 关联边界 + /// 边界环列表(一个边界环里所有的对象组成一个 DBObjectCollection) + public static List CreateBoundarys(this Hatch hatch) + { + if (!hatch.Id.IsOk()) + throw new ArgumentException("填充未加入或不存在于数据库"); + + var listDbObjColl = new List(); + + for (int i = 0; i < hatch.NumberOfLoops; i++) + { + var objColl = new DBObjectCollection(); + var loop = hatch.GetLoopAt(i); + bool isCurve2d = true; + + if (loop.IsPolyline) + { + // 判断为圆形: + // 上下两个圆弧,然后填充,就会生成此种填充 + // 顶点数是3,凸度是半圆,两个半圆就是一个圆形 + if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 || + loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1) + { + var center = PointEx.GetMidPointTo(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex); + var radius = loop.Polyline[0].Vertex.GetDistanceTo(loop.Polyline[1].Vertex) * 0.5; + var circle = new Circle(center.Point3d(), Vector3d.ZAxis, radius); + objColl.Add(circle); + } + else + { + // 遍历多段线信息 + var bvc = loop.Polyline; + var pl = new Polyline(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < bvc.Count; j++) + { + var bvw = new BulgeVertexWidth(bvc[j]); + pl.AddVertexAt(j, bvw.Vertex, bvw.Bulge, bvw.StartWidth, bvw.EndWidth); + } + objColl.Add(pl); + } + + isCurve2d = false; + } + else if (loop.Curves.Count == 2) // 1是不可能的,大于2的是曲线; 边界是曲线,过滤可能是圆形的情况 + { + if (loop.Curves.Count != 2) { + throw new ArgumentException( + "边界非多段线,而且点数!=2,点数为:" + nameof(loop.Curves.Count) + ";两个矩形交集的时候会出现此情况."); + } + + // 判断为圆形: + // 用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充 + // 边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中 + + // 第一段 + var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); // 曲线取样点分两份(3点) + var mid1Pt = getCurves1Pts[1]; // 腰点 + double bulge1 = loop.Curves[0].StartPoint.GetArcBulge(mid1Pt, loop.Curves[0].EndPoint); + + // 第二段 + var getCurves2Pts = loop.Curves[1].GetSamplePoints(3); + var mid2Pt = getCurves2Pts[1]; + double bulge2 = loop.Curves[1].StartPoint.GetArcBulge(mid2Pt, loop.Curves[1].EndPoint); + + // 第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧 + if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1) + { + var center = PointEx.GetMidPointTo(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); + var radius = loop.Curves[0].StartPoint.GetDistanceTo(loop.Curves[1].StartPoint) * 0.5; + var circle = new Circle(center.Point3d(), Vector3d.ZAxis, radius); + objColl.Add(circle); + isCurve2d = false; + } + } + + // 边界是曲线 + if (isCurve2d) + { + int pLineCount = 0; //记录多段线数量 + int curveIsClosed = 0; // 取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段 + bool newPline = true; // 是否开始新的多段线(一个边界中可能有多条多段线) + bool firstIsPline = false; //遍历边界的第一个子段为多段线(遍历时不一定从多段线的首段开始) + List? polyLineVertexs = null; + List> polyLineData = new(); + + // 遍历边界的多个子段 + foreach (Curve2d curve in loop.Curves) + { + // 计数用于实现闭合 + curveIsClosed++; + + if (curve is CircularArc2d or LineSegment2d) + { + var pts = curve.GetSamplePoints(3); + var midPt = pts[1]; + + // 判断为多段线圆: + // 首尾相同,就是圆形 + if (curve.StartPoint.IsEqualTo(curve.EndPoint, new Tolerance(1e-6, 1e-6))) + { + // 获取起点,然后采样三点,中间就是对称点(直径点) + var center = PointEx.GetMidPointTo(curve.StartPoint, midPt); + var radius = curve.StartPoint.GetDistanceTo(midPt) * 0.5; + var circle = new Circle(center.Point3d(), Vector3d.ZAxis, radius); + objColl.Add(circle); + // 添加在中部的多段线末尾点 + if (curveIsClosed > 1 && !newPline) + polyLineVertexs?.Add(new BulgeVertexWidth(curve.StartPoint, 0)); + // 开始新的多段线 + newPline = true; + continue; + } + + if (curveIsClosed == 1) + firstIsPline = true; + + if (newPline) + { + polyLineVertexs = new(); + polyLineData.Add(polyLineVertexs); + newPline = false; + pLineCount++; + } + + // 判断为多段线,圆弧或直线: + double bulge = curve.StartPoint.GetArcBulge(midPt, curve.EndPoint); + polyLineVertexs?.Add(new BulgeVertexWidth(curve.StartPoint, bulge)); + + // 末尾点,不闭合的情况下就要获取这个 + if (curveIsClosed == loop.Curves.Count) + { + if (firstIsPline && pLineCount > 1) + { + // 连接首尾多段线 + polyLineData[0].ForEach(bvw => polyLineData[^1].Add(bvw)); + polyLineData.RemoveAt(0); + } + else + polyLineVertexs?.Add(new BulgeVertexWidth(curve.EndPoint, 0)); + } + + continue; + } + + // 判断为样条曲线: + if (curve is NurbCurve2d spl) + objColl.Add(spl.ToCurve()); + + // 判断为椭圆: + if (curve is EllipticalArc2d eArc2d) + { + var startParam = eArc2d.IsClockWise ? -eArc2d.EndAngle : eArc2d.StartAngle; + var endParam = eArc2d.IsClockWise ? -eArc2d.StartAngle : eArc2d.EndAngle; + var ellipse = new Ellipse( + eArc2d.Center.Point3d(), + Vector3d.ZAxis, + eArc2d.MajorAxis.Convert3d() * eArc2d.MajorRadius, + eArc2d.MinorRadius / eArc2d.MajorRadius, + Math.Atan2(Math.Sin(startParam) * eArc2d.MinorRadius, + Math.Cos(startParam) * eArc2d.MajorRadius), + Math.Atan2(Math.Sin(endParam) * eArc2d.MinorRadius, + Math.Cos(endParam) * eArc2d.MajorRadius)); + objColl.Add(ellipse); + } + + // 添加在中部的多段线末尾点 + if (curveIsClosed > 1 && !newPline) + polyLineVertexs?.Add(new BulgeVertexWidth(curve.StartPoint, 0)); + // 开始新的多段线 + newPline = true; + } + + // 生成多段线 + polyLineData.ForEach(bvws => + { + if (bvws.Count == 0) return; + var pl = new Polyline(); + pl.SetDatabaseDefaults(); + for (int j = 0; j < bvws.Count; j++) + pl.AddVertexAt(j, bvws[j].Vertex, bvws[j].Bulge, bvws[j].StartWidth, bvws[j].EndWidth); + objColl.Add(pl); + }); + } + + listDbObjColl.Add(objColl); + }; + + return listDbObjColl; + } + + /// + /// 对已存在于数据库的填充重新设置边界并计算 + /// + /// + /// 边界对象(一个边界环里所有的对象 id 组成一个 ObjectIdCollection) + ///
边界必需是封闭的环, 可以是一条线围合而成也可以是多条线首尾相连围合而成
+ ///
多个外边界的时候, 建议顺序为(外边界,外边界,外边界,普通边界....), 或(外边界, 普通边界.....外边界, 普通边界....)
+ /// + /// 关联边界(默认保持原样) + public static void ResetBoundarys(this Hatch hatch, + List boundaryIds, bool? associative = null) + { + if (!hatch.Id.IsOk()) + throw new ArgumentException("填充未加入或不存在于数据库"); + + boundaryIds.ForEach(ids => + { + foreach (ObjectId id in ids) + { + if (!id.IsOk()) + throw new ArgumentException("边界未加入或不存在于数据库"); + } + }); + + using (hatch.ForWrite()) + { + while (hatch.NumberOfLoops > 0) + hatch.RemoveLoopAt(0); + + if (associative != null) + hatch.Associative = associative == true; + + var isOutermost = true; + + boundaryIds.ForEach(ids => + { + try + { + // 要先添加最外面的边界 + if (isOutermost) + { + isOutermost = false; + hatch.AppendLoop(HatchLoopTypes.Outermost, ids); + } + else + { + // HatchLoopTypes.External 似乎比 HatchLoopTypes.Default 更不容易出问题 + hatch.AppendLoop(HatchLoopTypes.External, ids); + } + } + catch (Exception ex) + { + Env.Editor.WriteMessage(Environment.NewLine + + "发生错误,传入的边界不符合要求,请核实传入的边界是否为封闭的"); + throw new Exception(ex.Message); + } + }); + + hatch.EvaluateHatch(true); + } + } } \ No newline at end of file