using System; using System.Collections.Generic; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using CadParamPluging.Common; namespace CadParamPluging.Cad { public static class BlockRawFreeForgeNoRoundHeadDrawer { public static void Draw(CadContext ctx, ParamBag bag, Point3d center, double scaleFactor = 1.0) { if (ctx == null) throw new ArgumentNullException(nameof(ctx)); if (bag == null) throw new ArgumentNullException(nameof(bag)); var effectiveBag = scaleFactor < 1.0 ? ScaleParamBag(bag, scaleFactor) : bag; var db = ctx.Database; var tr = ctx.Transaction; var prevDb = HostApplicationServices.WorkingDatabase; HostApplicationServices.WorkingDatabase = db; BlockTableRecord btr; try { var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); } finally { HostApplicationServices.WorkingDatabase = prevDb; } var context = new FeatureDrivenDrawer.DrawingContext { Ctx = ctx, Bag = effectiveBag, OriginalBag = bag, Center = center, DeliveryStatus = "毛料态", StructuralFeature = "方体", SpecialCondition = "无圆头", // Updated ProcessMethod = "自由锻", Btr = btr, Style = new DrawingStyleManager(db, tr) }; DrawCore(context); } // COPIED FROM FeatureDrivenDrawer.DrawBlockRawFreeForgeRoundHeadCore AND MODIFIED private static void DrawCore(FeatureDrivenDrawer.DrawingContext ctx) { var bag = ctx.Bag; // BoxSize1 (Width - 总长度) // BoxSize2 (Height - 高度/直径) var width = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1); var height = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2); if (!width.HasValue || width.Value <= 0 || !height.HasValue || height.Value <= 0) { return; } double H = height.Value; double W = width.Value; // 外轮廓圆弧:高度固定为 H/5 -> 原"有圆头"逻辑 // 依据用户新需求 (2026-01-29): 黄色部分(圆弧高)是粉色部分(总高H)的1/5 // double arcHeight = H / 5.0; // OLD Round Head Logic // 【关键修改点】无圆头模式:圆弧高度为0 double arcHeight = 0; // 视觉比例:总宽度映射 double visualTotalW = H * 3.0; // 保持视觉宽高比约 3:1 // 绘图坐标计算 (保持居中) double ox = ctx.Center.X - visualTotalW / 2.0; // 左边界 (圆弧最左端) double oy = ctx.Center.Y - H / 2.0; // 底边 // 内框边界(从左圆弧弦线到右圆弧弦线) // 注意:ox 是圆弧顶点,xInnerLeft 应该是弦线位置 double xInnerLeft = ox + arcHeight; double xInnerRight = ox + visualTotalW - arcHeight; double innerWidth = xInnerRight - xInnerLeft; // 获取内框圆角参数:优先取 UnspecifiedFilletRadiusMax,如果没有则取 BoxFilletRadiusMax var innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyUnspecifiedFilletRadiusMax); if (!innerFilletParam.HasValue || innerFilletParam.Value <= 0) { innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxFilletRadiusMax); } double innerFilletR = innerFilletParam.HasValue && innerFilletParam.Value > 0 ? innerFilletParam.Value : 0; // 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧) // 修正:圆弧弦线需向内缩进 innerFilletR,以与锻件圆角起点对齐 // 【关键修改点】无圆头模式:不需要绘制大圆弧外轮廓,或者绘制一个直角的轮廓。 // 由于 DrawBoxSideFrame 已经绘制了左右两侧的边框线(含圆角), // DrawBoxSectionContourWithFillet 绘制了中间。 // 它们的合集就是外轮廓。 // 原代码 DrawBlockRoundHeadOutline 是为了画那个额外的“耳朵”圆弧。 // 这里我们只需要一个简单的矩形外框作为“外轮廓实体”(如果需要) // 或者就像原逻辑一样,如果没有大圆弧,就不画 DrawBlockRoundHeadOutline。 // 但用户要求“包含尺寸标注和参数标注线都复制过来”,且标注依赖于这些几何位置。 // 这里我们跳过 DrawBlockRoundHeadOutline,因为那是特定的圆头形状。 // 如果用户想要最外侧也是封闭的线,SideFrames + SectionContour 其实不完全封闭(中间有重叠线)。 // 让我们看看原逻辑:DrawBlockRoundHeadOutline 是独立的一圈粗线。 // 此处我们是否需要一个“无圆头”的外圈粗线? // 我们可以仅依靠内部组件,或者画一个大的圆角矩形包围所有。 // 鉴于 SideFrame 已经画了外侧线,我们这里可能不需要额外的 Outline, // UNLESS the user implies "No Round Head" means "Rectangular Head". // Let's assume the Side Frames define the shape. // DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, innerFilletR); // SKIPPED // 2. 绘制分段式内框(左、中、右三段)以支持中间剖面区域的独立圆角 // 中间区域(剖面)作为主体,如果设置了圆角,四个角都应为圆角 // 计算视觉上的剖面宽度 // 依据用户需求:尺寸3 (BoxSize3) 对应剖面的宽 var size3 = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3); double visualSectionWidth; if (size3.HasValue && size3.Value > 0) { // 按比例映射:VisualW / PhysicalW double scale = visualTotalW / W; visualSectionWidth = size3.Value * scale; if (visualSectionWidth > innerWidth * 0.95) visualSectionWidth = innerWidth * 0.95; } else { visualSectionWidth = innerWidth / 3.0; } // 居中布置剖面 double sectionOffset = (innerWidth - visualSectionWidth) / 2.0; double xSectionLeft = xInnerLeft + sectionOffset; double xSectionRight = xSectionLeft + visualSectionWidth; // 绘制中间剖面框(带圆角 - 根据需求保留) DrawBoxSectionContourWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR); // 绘制左侧框线(连接左端圆弧和中间剖面框) // 用户需求: // 1. 去掉最外侧的圆角 -> filletR 传入 0 // 2. 横向闭合:水平线需延伸到中间框圆角结束的地方 (xSectionLeft + innerFilletR) 以实现无缝连接 // 3. 竖向不闭合:DrawBoxSideFrame 内部不 Closed,且不画连接处的竖线 DrawBoxSideFrame(ctx, xInnerLeft, xSectionLeft + innerFilletR, oy, H, 0, isLeft: true); // 绘制右侧框线 // 类似的,水平线起点延伸到 (xSectionRight - innerFilletR) DrawBoxSideFrame(ctx, xSectionRight - innerFilletR, xInnerRight, oy, H, 0, isLeft: false); // 3. 剖面填充 - 只在中间区域 if (innerFilletR > 0.01) { DrawRingSectionHatchWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR); } else { DrawBlockSectionHatch(ctx, xSectionLeft, xSectionRight, oy, H); } // 5. 零件轮廓框(黄色双点划线矩形 - 在内框内,与锻件有一定间距) var widthPrime = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1Prime); var heightPrime = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2Prime); if (widthPrime.HasValue && widthPrime.Value > 0 && heightPrime.HasValue && heightPrime.Value > 0) { double partH = heightPrime.Value; double partW = widthPrime.Value; // 计算零件在视觉上的宽度比例 double partWidthRatio = partW / W; double visualPartW = innerWidth * partWidthRatio; if (visualPartW > innerWidth) visualPartW = innerWidth * 0.9; // 零件在锻件内居中 double partOffsetX = (innerWidth - visualPartW) / 2.0; double partOffsetY = (H - partH) / 2.0; double xPartLeft = xInnerLeft + partOffsetX; double yPartBottom = oy + partOffsetY; DrawBlockPartContour(ctx, xPartLeft, yPartBottom, visualPartW, partH); } // 6. 宽度标注 (BoxSize1) - 位于底部 // 标注从锻件最左端到最右端(包括圆头) { var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1) ?? W; var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1); var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1TolPlus); var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1TolMinus); var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1TolMinus); var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方,用括号包裹 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})"; } // 标注点从内框左右边界开始(连接到矩形内框) - Modify Y to center to ensure extension lines touch the geometry double dimOriginY = oy + innerFilletR; AddLinearDim( ctx, new Point3d(xInnerLeft, dimOriginY, 0), new Point3d(xInnerRight, dimOriginY, 0), new Point3d((xInnerLeft + xInnerRight) / 2, oy - 25, 0), 0, dimText); } // 7. 高度标注 (BoxSize2) - 位于右侧 // 标注从锻件顶部到底部 { var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2) ?? H; var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2); var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2TolPlus); var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2TolMinus); var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2TolMinus); var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})"; } // 标注从圆弧最外侧开始(避免与圆弧轮廓线重叠) double xOuterRight = ox + visualTotalW; // 修正:由于去掉了最外侧圆角,标注界线起点直接对齐最外侧边界 double xDimOrigin = xOuterRight; AddLinearDim( ctx, new Point3d(xDimOrigin, oy, 0), new Point3d(xDimOrigin, oy + H, 0), new Point3d(xOuterRight + 20, oy + H / 2, 0), 90, dimText); } // 8. 中间矩形宽度标注 (BoxSize3) - 位于上方 // 对应剖面区域宽度 { var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3) ?? 0; var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3); if (val > 0) { var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3TolPlus); var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3TolMinus); var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3TolMinus); var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})"; } AddLinearDim( ctx, new Point3d(xSectionLeft, oy + H, 0), new Point3d(xSectionRight, oy + H, 0), new Point3d((xSectionLeft + xSectionRight) / 2, oy + H + 15, 0), 0, dimText); } } // 9. 硬度符号 - 从剖面底部引出 var hardnessVal = bag.GetString(FeatureDrivenDrawer.KeyHardness); if (!string.IsNullOrWhiteSpace(hardnessVal)) { double xStart = (xSectionLeft + xSectionRight) / 2.0; double yStart = oy; DrawHardnessSymbol(ctx, xStart, yStart); } // 10. 标刻内容引线 - 从剖面顶部引出 var markingText = bag.GetString(FeatureDrivenDrawer.KeyMarkingContent); if (!string.IsNullOrWhiteSpace(markingText)) { double xTarget = (xSectionLeft + xSectionRight) / 2.0; double yTarget = oy + H; DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText); } } // =============== HELPERS COPIED FROM FeatureDrivenDrawer AND ADAPTED =============== private static void DrawBlockPartContour(FeatureDrivenDrawer.DrawingContext ctx, double x, double y, double width, double height) { try { var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(x, y), 0, 0, 0); poly.AddVertexAt(1, new Point2d(x + width, y), 0, 0, 0); poly.AddVertexAt(2, new Point2d(x + width, y + height), 0, 0, 0); poly.AddVertexAt(3, new Point2d(x, y + height), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); } catch {} } private static void DrawBoxSectionContourWithFillet(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height, double filletR) { var yTop = yBottom + height; var width = xRight - xLeft; var maxR = Math.Min(width * 0.5, height * 0.5); var r = Math.Min(filletR, maxR); if (r <= 0.01) { var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(xLeft, yBottom), 0, 0, 0); poly.AddVertexAt(1, new Point2d(xRight, yBottom), 0, 0, 0); poly.AddVertexAt(2, new Point2d(xRight, yTop), 0, 0, 0); poly.AddVertexAt(3, new Point2d(xLeft, yTop), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); return; } var bulge = Math.Tan(Math.PI / 8.0); var polyFillet = new Polyline(); polyFillet.AddVertexAt(0, new Point2d(xLeft + r, yBottom), 0, 0, 0); polyFillet.AddVertexAt(1, new Point2d(xRight - r, yBottom), bulge, 0, 0); polyFillet.AddVertexAt(2, new Point2d(xRight, yBottom + r), 0, 0, 0); polyFillet.AddVertexAt(3, new Point2d(xRight, yTop - r), bulge, 0, 0); polyFillet.AddVertexAt(4, new Point2d(xRight - r, yTop), 0, 0, 0); polyFillet.AddVertexAt(5, new Point2d(xLeft + r, yTop), bulge, 0, 0); polyFillet.AddVertexAt(6, new Point2d(xLeft, yTop - r), 0, 0, 0); polyFillet.AddVertexAt(7, new Point2d(xLeft, yBottom + r), bulge, 0, 0); polyFillet.Closed = true; ctx.Style?.Apply(polyFillet, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(polyFillet); ctx.Tr.AddNewlyCreatedDBObject(polyFillet, true); } private static void DrawBoxSideFrame(FeatureDrivenDrawer.DrawingContext ctx, double xStart, double xEnd, double yBottom, double height, double filletR, bool isLeft) { var yTop = yBottom + height; var width = Math.Abs(xEnd - xStart); if (width < 0.1) return; var maxR = Math.Min(width * 0.5, height * 0.5); var r = Math.Min(filletR, maxR); var bulge = -Math.Tan(Math.PI / 8.0); var poly = new Polyline(); // 为了闭合缝隙处理: // 中间剖面框 (DrawBoxSectionContourWithFillet) 绘制在 xSectionLeft ~ xSectionRight // 左侧框线 DrawBoxSideFrame(..., xInnerLeft, xSectionLeft, ...) 是左边的部分 // 右侧框线 DrawBoxSideFrame(..., xSectionRight, xInnerRight, ...) 是右边的部分 // // 问题在于:当 SideFrame 的 filletR=0 时,它画的是纯直线矩形。 // 而中间的 DrawBoxSectionContourWithFillet 依然可能有 innerFilletR > 0,导致它的角是圆的。 // 当两个矩形拼接时,如果中间那个有圆角,而旁边的是直角,交界处的高度看起来是一样的(yBottom, yTop),理论上应该重合。 // 但如果中间框的圆角导致其垂直边缩短(圆角起止点变化),而SideFrame只是简单的画到 yTop/yBottom, // 它们在垂直方向上是重合的。 // // 观察截图,缝隙似乎出现在连接处。 // 中间框 DrawBoxSectionContourWithFillet 在 xLeft, xRight 处的垂直线段是: // 左侧: (xLeft, yBottom+r) 到 (xLeft, yTop-r) // 右侧: (xRight, yBottom+r) 到 (xRight, yTop-r) // // SideFrame (以左侧为例, isLeft=true, filletR=0): // xEnd 是连接点 (即 xLeft)。 // 它画的线包含 (xEnd, yTop) -> (xEnd, yBottom) // // 理论上 (xEnd, yBottom) 和 (xSectionLeft, yBottom+r) 之间有高度差 r,但这部分重叠区域应该由圆弧填补? // 不,DrawBoxSectionContourWithFillet 画了完整的闭合回路,包括圆角。 // SideFrame 画了另一侧。 // 只要 SideFrame 的垂直边完整覆盖 (yBottom 到 yTop),就不应该有空隙,除非 SideFrame 的 x 坐标没对上。 // // 另一种可能是:DrawBoxSectionContourWithFillet 的圆角不仅影响y,也影响x?不,圆角是在角内切的。 // // 让我们仔细看 DrawBoxSectionContourWithFillet 的左边线: // Vertex 6: (xLeft, yTop - r) // Vertex 7: (xLeft, yBottom + r) // 这两点之间是直线。 // 下方圆角从 (xLeft, yBottom+r) 到 (xLeft+r, yBottom)。 // 这意味着在 xLeft 这一条垂直线上,只有 yBottom+r 到 yTop-r 这一段是有线的。 // yBottom 到 yBottom+r 这一段是空的(因为线往右弯去画圆角了)。 // // 而 SideFrame (isLeft=true, filletR=0) 在 xEnd (即 xLeft) 处的线是: // Vertex 0: (xEnd, yBottom) // Vertex ... (最终回到 xEnd, yTop) // 如果 SideFrame 是开放的 (Closed=false),我们来看看它的路径。 // isLeft=true: // P0: (xEnd, yBottom) // ... 画左边的框 ... // P4: (xEnd, yTop) // 它画了两条水平线回到 center,但是没有画连接这两个点的垂直线! // 原逻辑可能是依赖中间框来画这条线? // 是的,FeatureDrivenDrawer 里的逻辑通常是一块块拼接,共用边如果不画两遍的话。 // // 修正方案: // 无论中间框是否有圆角,SideFrame 作为“框线”,应该把与中间框接触的那条边补上,或者延伸进去。 // 既然中间框是有圆角的,那么在角的地方,中间框的线是缩进去的。 // SideFrame 必须画出完整的一条垂直线 (xEnd, yBottom) 到 (xEnd, yTop) 来“封口”, // 虽然这会导致与中间框的直线部分重叠,但能盖住圆角留下的空隙。 // // 当前代码 isLeft=true 时: // poly.AddVertexAt(0, new Point2d(xEnd, yBottom), 0, 0, 0); // ... (往左画) ... // poly.AddVertexAt(5, new Point2d(xEnd, yTop), 0, 0, 0); // 确实是开口的“C”形。 // // 我们需要让它闭合,或者至少画上那条连接线。 // 直接将 poly.Closed 设为 true 即可! // 这样 (xEnd, yTop) 会自动连回 (xEnd, yBottom),形成一个完整的矩形框。 // 这样右边那条垂直线就会被画出来,填补中间框圆角留下的上下空缺。 if (isLeft) { // 左侧框:起点在右下 (与中间框接壤处),顺时针绘制 // P0: (xEnd, yBottom) poly.AddVertexAt(0, new Point2d(xEnd, yBottom), 0, 0, 0); if (r > 0.01) { poly.AddVertexAt(1, new Point2d(xStart + r, yBottom), bulge, 0, 0); poly.AddVertexAt(2, new Point2d(xStart, yBottom + r), 0, 0, 0); poly.AddVertexAt(3, new Point2d(xStart, yTop - r), bulge, 0, 0); poly.AddVertexAt(4, new Point2d(xStart + r, yTop), 0, 0, 0); poly.AddVertexAt(5, new Point2d(xEnd, yTop), 0, 0, 0); } else { poly.AddVertexAt(1, new Point2d(xStart, yBottom), 0, 0, 0); poly.AddVertexAt(2, new Point2d(xStart, yTop), 0, 0, 0); poly.AddVertexAt(3, new Point2d(xEnd, yTop), 0, 0, 0); } } else { // 右侧框:起点在左上 (与中间框接壤处),顺时针绘制 // P0: (xStart, yTop) poly.AddVertexAt(0, new Point2d(xStart, yTop), 0, 0, 0); if (r > 0.01) { poly.AddVertexAt(1, new Point2d(xEnd - r, yTop), bulge, 0, 0); poly.AddVertexAt(2, new Point2d(xEnd, yTop - r), 0, 0, 0); poly.AddVertexAt(3, new Point2d(xEnd, yBottom + r), bulge, 0, 0); poly.AddVertexAt(4, new Point2d(xEnd - r, yBottom), 0, 0, 0); poly.AddVertexAt(5, new Point2d(xStart, yBottom), 0, 0, 0); } else { poly.AddVertexAt(1, new Point2d(xEnd, yTop), 0, 0, 0); poly.AddVertexAt(2, new Point2d(xEnd, yBottom), 0, 0, 0); poly.AddVertexAt(3, new Point2d(xStart, yBottom), 0, 0, 0); } } // 关键修正:闭合多段线,确保与中间框接触的垂直边被画出来,消除圆角导致的缝隙 poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); } private static void DrawRingSectionHatchWithFillet(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height, double filletR) { try { var yTop = yBottom + height; var width = xRight - xLeft; var maxR = Math.Min(width * 0.5, height * 0.5); var r = Math.Min(filletR, maxR); var bulge = Math.Tan(Math.PI / 8.0); var boundary = new Polyline(); if (r > 0.01) { boundary.AddVertexAt(0, new Point2d(xLeft + r, yBottom), 0, 0, 0); boundary.AddVertexAt(1, new Point2d(xRight - r, yBottom), bulge, 0, 0); boundary.AddVertexAt(2, new Point2d(xRight, yBottom + r), 0, 0, 0); boundary.AddVertexAt(3, new Point2d(xRight, yTop - r), bulge, 0, 0); boundary.AddVertexAt(4, new Point2d(xRight - r, yTop), 0, 0, 0); boundary.AddVertexAt(5, new Point2d(xLeft + r, yTop), bulge, 0, 0); boundary.AddVertexAt(6, new Point2d(xLeft, yTop - r), 0, 0, 0); boundary.AddVertexAt(7, new Point2d(xLeft, yBottom + r), bulge, 0, 0); } else { boundary.AddVertexAt(0, new Point2d(xLeft, yBottom), 0, 0, 0); boundary.AddVertexAt(1, new Point2d(xRight, yBottom), 0, 0, 0); boundary.AddVertexAt(2, new Point2d(xRight, yTop), 0, 0, 0); boundary.AddVertexAt(3, new Point2d(xLeft, yTop), 0, 0, 0); } boundary.Closed = true; ctx.Btr.AppendEntity(boundary); ctx.Tr.AddNewlyCreatedDBObject(boundary, true); var hatch = new Hatch(); hatch.SetDatabaseDefaults(); hatch.Normal = new Vector3d(0, 0, 1); hatch.Elevation = 0.0; ctx.Btr.AppendEntity(hatch); ctx.Tr.AddNewlyCreatedDBObject(hatch, true); hatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31"); hatch.PatternScale = 10; hatch.PatternAngle = 0; hatch.Associative = false; ctx.Style?.Apply(hatch, DrawingStyleManager.Role.Hatch); var ids = new ObjectIdCollection(); ids.Add(boundary.ObjectId); hatch.AppendLoop(HatchLoopTypes.External, ids); hatch.EvaluateHatch(true); boundary.Erase(true); } catch {} } private static void DrawBlockSectionHatch(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height) { DrawRingSectionHatchWithFillet(ctx, xLeft, xRight, yBottom, height, 0); } private static ParamBag ScaleParamBag(ParamBag original, double scaleFactor) { if (original == null) return null; if (Math.Abs(scaleFactor - 1.0) < 0.000001) return original; var newBag = new ParamBag(); var scaleKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { FeatureDrivenDrawer.KeyBoxSize1, FeatureDrivenDrawer.KeyBoxSize2, FeatureDrivenDrawer.KeyBoxSize3, FeatureDrivenDrawer.KeyBoxSize1TolPlus, FeatureDrivenDrawer.KeyBoxSize1TolMinus, FeatureDrivenDrawer.KeyBoxSize2TolPlus, FeatureDrivenDrawer.KeyBoxSize2TolMinus, FeatureDrivenDrawer.KeyBoxSize3TolPlus, FeatureDrivenDrawer.KeyBoxSize3TolMinus, FeatureDrivenDrawer.KeyBoxFilletRadiusMax, FeatureDrivenDrawer.KeyUnspecifiedFilletRadiusMax, FeatureDrivenDrawer.KeyBoxSize1Prime, FeatureDrivenDrawer.KeyBoxSize2Prime, FeatureDrivenDrawer.KeyBoxSize3Prime }; foreach (var key in original.GetKeys()) { var strVal = original.GetString(key); if (scaleKeys.Contains(key) && double.TryParse(strVal, out var val)) { newBag.Set(key, (val * scaleFactor).ToString("0.###")); } else { newBag.Set(key, strVal); } } return newBag; } private static void AddLinearDim(FeatureDrivenDrawer.DrawingContext ctx, Point3d p1, Point3d p2, Point3d ptDim, double rot, string text) { try { var dim = new RotatedDimension(); dim.SetDatabaseDefaults(); dim.XLine1Point = p1; dim.XLine2Point = p2; dim.DimLinePoint = ptDim; dim.Rotation = rot * Math.PI / 180.0; dim.DimensionStyle = ctx.Db.Dimstyle; dim.DimensionText = text; // Color Index 3 = Green dim.ColorIndex = 3; // --- 强制应用覆盖属性以确保显示效果 --- // 颜色覆盖:尺寸线、尺寸界线、文字全部强制绿色 // Dimclrd: Dimension line color // Dimclre: Extension line color // Dimclrt: Text color // 3 = Green SetDimColor(dim, "Dimclrd", 3); SetDimColor(dim, "Dimclre", 3); SetDimColor(dim, "Dimclrt", 3); // 强制绘制尺寸线即使文字在界线外 (Dimtofl = true) SetDimBool(dim, "Dimtofl", true); // 文字/箭头最佳调整 (Dimatfit = 3: Best fit) SetDimInt(dim, "Dimatfit", 3); // 文字垂直位置 (Dimtad = 1: Above dimension line) SetDimInt(dim, "Dimtad", 1); // 文字水平对齐 (Dimtih = false: Aligned with dimension line, not horizontal) SetDimBool(dim, "Dimtih", false); SetDimBool(dim, "Dimtoh", false); // 尺寸参数微调 SetDimDouble(dim, "Dimtxt", 3.5); // 文字高度 SetDimDouble(dim, "Dimasz", 2.5); // 箭头大小 SetDimDouble(dim, "Dimgap", 1.0); // 文字偏移 SetDimDouble(dim, "Dimexe", 1.0); // 延伸超出量 SetDimDouble(dim, "Dimexo", 0.0); // 原点偏移 ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension); // 再次确保 Entity 颜色为绿色 dim.ColorIndex = 3; ctx.Btr.AppendEntity(dim); ctx.Tr.AddNewlyCreatedDBObject(dim, true); // 尝试重计算以刷新显示块 try { dim.RecomputeDimensionBlock(true); } catch {} } catch {} } // 辅助反射设置方法 (Copy from FeatureDrivenDrawer to avoid dependency issues or access restrictions) private static void SetDimColor(Dimension dim, string prop, int colorIndex) { try { // Property type for color override is usually Autodesk.AutoCAD.Colors.Color // But specifically for Dimclrd/e/t properties on the object wrapper, they might be exposed as Color objects. // However, standard COM/ActiveX way involves variables. In .NET API, we usually set Dim Variables on the Database or style. // BUT, effectively, we can set overrides on the Dimension entity itself if it supports the property via reflection // typically mirroring the system variable names. // Let's rely on the property reflection if available, but for color, it's tricky. // The most robust way using .NET API for dimension overrides is strictly through the properties if exposed, // OR modifying the XData/Dxf codes. // // WAIT. FeatureDrivenDrawer uses TrySetDimProp with reflection. Let's replicate that simple logic. // Note: Dimension class properties in .NET don't map 1:1 to DIM vars (e.g. Dimclrd is not a property of Dimension). // They are properties of DimensionStyle. To override on an instance, we strictly need to simple properties if they exist? // Actually, the previous code used reflection on 'dim' object. // Let's double check if "Dimclrd" exists on RotatedDimension. It usually doesn't. // It holds "DimColor" maybe? No. // // Re-reading FeatureDrivenDrawer.TrySetDimProp ... // It sets properties via reflection. // Let's implement a safer version: using dynamic if possible, or just standard properties. // Actually, correct way to override dim var on an entity is NOT via properties (they don't exist), // but by creating a DimStyle override or setting specific properties like DimensionText. // HOWEVER, RotatedDimension DOES have properties like 'Dimclrd' EXPOSED dynamically in some contexts? // No, standard .NET API does not expose 'Dimclrd' as a property on RotatedDimension. // // Wait, FeatureDrivenDrawer code shown in Step 182 DOES NOT show TrySetDimProp setting 'Dimclrd'. // Step 200 shows TryApplyDimSizeOverrides calling TrySetDimProp(dim, "Dimclrd", 3). // This implies that at runtime, the Dimension object (or a wrapper) HAS these properties? // OR, the User's environment has an extension method? // OR, FeatureDrivenDrawer.TrySetDimProp handles it magic? // Let's look at FeatureDrivenDrawer.TrySetDimProp in Step 182. // It does `obj.GetType().GetProperty(propName)`. // If this works, then RotatedDimension must have these properties. // Let's assume they exist as per the working example in FeatureDrivenDrawer. var p = dim.GetType().GetProperty(prop); if (p != null && p.CanWrite) { if (p.PropertyType == typeof(Autodesk.AutoCAD.Colors.Color)) { p.SetValue(dim, Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByAci, (short)colorIndex), null); } else if (p.PropertyType == typeof(int)) { p.SetValue(dim, colorIndex, null); } } } catch {} } private static void SetDimBool(Dimension dim, string prop, bool val) { try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {} } private static void SetDimDouble(Dimension dim, string prop, double val) { try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {} } private static void SetDimInt(Dimension dim, string prop, int val) { try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {} } private static string FormatDimNumber(double value, string rawInput = null) { try { // If rawInput is provided and matches the value, use its precision if (!string.IsNullOrWhiteSpace(rawInput) && double.TryParse(rawInput, out var d) && Math.Abs(d - value) < 0.000001) { int decIndex = rawInput.IndexOf('.'); int decimals = 0; if (decIndex >= 0) { decimals = rawInput.Length - decIndex - 1; } if (decimals == 0) return value.ToString("0"); return value.ToString("0." + new string('0', decimals)); } // Fallback: auto-detect from value (strip trailing zeros) string str = value.ToString("0.##########"); int decimalIndex = str.IndexOf('.'); if (decimalIndex < 0) { return str; // Integer, no decimal } int autoDecimals = str.Length - decimalIndex - 1; if (autoDecimals == 0) return str; string format = "0." + new string('0', autoDecimals); return value.ToString(format); } catch { return value.ToString(); } } private static string BuildDimensionText(string baseText, double? tolPlus, double? tolMinus, string tolPlusStr = null, string tolMinusStr = null) { if (!tolPlus.HasValue && !tolMinus.HasValue) { return baseText; } bool isSymmetrical = false; if (tolPlus.HasValue && tolMinus.HasValue) { if (Math.Abs(Math.Abs(tolPlus.Value) - Math.Abs(tolMinus.Value)) < 0.000001) { isSymmetrical = true; } } if (isSymmetrical) { var val = Math.Abs(tolPlus.Value); return $"{baseText}%%p{FormatDimNumber(val, tolPlusStr)}"; } const double tolScale = 0.7; var plusStr = string.Empty; var minusStr = string.Empty; if (tolPlus.HasValue) { plusStr = tolPlus.Value >= 0 ? $"+{FormatDimNumber(tolPlus.Value, tolPlusStr)}" : $"{FormatDimNumber(tolPlus.Value, tolPlusStr)}"; } if (tolMinus.HasValue) { double vm = tolMinus.Value; string formattedVal = FormatDimNumber(Math.Abs(vm), tolMinusStr); if (Math.Abs(vm) < 0.000001) { minusStr = formattedVal; } else { minusStr = $"-{formattedVal}"; } } if (tolPlus.HasValue && tolMinus.HasValue) { return $"{baseText} {{\\H{tolScale}x;\\S{plusStr}^{minusStr};}}"; } var single = tolPlus.HasValue ? plusStr : minusStr; return $"{baseText} {{\\H{tolScale}x;{single}}}"; } private static void DrawHardnessSymbol(FeatureDrivenDrawer.DrawingContext ctx, double xStart, double yStart) { try { var p1 = new Point3d(xStart, yStart, 0); var p2 = new Point3d(xStart + 15, yStart - 15, 0); var p3 = new Point3d(p2.X + 10, p2.Y, 0); var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(p1.X, p1.Y), 0, 0, 0); poly.AddVertexAt(1, new Point2d(p2.X, p2.Y), 0, 0, 0); poly.AddVertexAt(2, new Point2d(p3.X, p3.Y), 0, 0, 0); ctx.Style?.Apply(poly, DrawingStyleManager.Role.Dimension); poly.ColorIndex = 3; ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); double radius = 4.0; var center = new Point3d(p3.X + radius, p3.Y, 0); var circle = new Circle(center, Vector3d.ZAxis, radius); circle.ColorIndex = 3; ctx.Btr.AppendEntity(circle); ctx.Tr.AddNewlyCreatedDBObject(circle, true); var text = new DBText(); text.TextString = "HB"; text.Height = 3.5; text.ColorIndex = 3; text.HorizontalMode = TextHorizontalMode.TextCenter; text.VerticalMode = TextVerticalMode.TextVerticalMid; text.AlignmentPoint = center; text.Position = center; ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch {} } private static void DrawSpecialHBLeaderToTop(FeatureDrivenDrawer.DrawingContext ctx, double xTarget, double yTarget, string textContent) { try { var p1 = new Point3d(xTarget, yTarget, 0); var p2 = new Point3d(xTarget - 10, yTarget + 10, 0); double textLen = (string.IsNullOrEmpty(textContent) ? 10 : textContent.Length) * 2.5; if (textLen < 20) textLen = 20; var p3 = new Point3d(p2.X - textLen, p2.Y, 0); var leader = new Leader(); leader.SetDatabaseDefaults(); leader.AppendVertex(p1); leader.AppendVertex(p2); leader.AppendVertex(p3); leader.HasArrowHead = true; leader.ColorIndex = 7; leader.Layer = "0"; ctx.Btr.AppendEntity(leader); ctx.Tr.AddNewlyCreatedDBObject(leader, true); var text = new DBText(); text.TextString = textContent; text.Height = 3.5; text.ColorIndex = 7; text.Layer = "0"; text.HorizontalMode = TextHorizontalMode.TextLeft; text.VerticalMode = TextVerticalMode.TextBottom; text.AlignmentPoint = new Point3d(p3.X, p3.Y + 1.0, 0); text.Position = text.AlignmentPoint; ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch {} } } }