using System; using System.Collections.Generic; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using CadParamPluging.Common; namespace CadParamPluging.Cad { public static class BlockRawFreeForgeRoundHeadDrawer { 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 = "有圆头", ProcessMethod = "自由锻", Btr = btr, Style = new DrawingStyleManager(db, tr) }; DrawBlockRawFreeForgeRoundHeadCore(context); } private static void DrawBlockRawFreeForgeRoundHeadCore(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; // 视觉比例:总宽度映射 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; // 获取圆头处圆角半径 (BoxRoundHeadFilletRadiusMax) - 用于外侧圆角 // 依据用户需求:外侧圆角由“圆头处圆角半径”控制 var headFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxRoundHeadFilletRadiusMax); double headFilletR = headFilletParam.HasValue && headFilletParam.Value > 0 ? headFilletParam.Value : 0; // 获取内框圆角参数:优先取 UnspecifiedFilletRadiusMax,如果没有则取 BoxFilletRadiusMax - 用于内部剖面 var innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyUnspecifiedFilletRadiusMax); if (!innerFilletParam.HasValue || innerFilletParam.Value <= 0) { innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxFilletRadiusMax); } double sectionFilletR = innerFilletParam.HasValue && innerFilletParam.Value > 0 ? innerFilletParam.Value : 0; // 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; // 绘制中间剖面框(带圆角) // 使用 sectionFilletR DrawBoxSectionContourWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, sectionFilletR); // Calculate effective head fillet R (clamped by side frame width) double sideFrameWidth = xSectionLeft - xInnerLeft; double maxR = Math.Min(sideFrameWidth * 0.5, H * 0.5); double effectiveHeadR = Math.Min(headFilletR, maxR); double Rm = (H * H) / (8.0 * arcHeight) + arcHeight / 2.0; double D_tangent = (Rm >= effectiveHeadR) ? Math.Sqrt(Math.Max(0, (Rm - effectiveHeadR) * (Rm - effectiveHeadR) - (H / 2.0 - effectiveHeadR) * (H / 2.0 - effectiveHeadR))) : (Rm - arcHeight); double xf_left = effectiveHeadR > 0 ? (xInnerLeft + Rm - arcHeight) - D_tangent : xInnerLeft; double xf_right = effectiveHeadR > 0 ? (xInnerRight - Rm + arcHeight) + D_tangent : xInnerRight; double xc_left = xInnerLeft + Rm - arcHeight; double fx_L_tang = effectiveHeadR > 0 ? xc_left + (Rm / (Rm - effectiveHeadR)) * (xf_left - xc_left) : xInnerLeft; double fy_L_tangBot = effectiveHeadR > 0 ? (oy + H / 2.0) + (Rm / (Rm - effectiveHeadR)) * ((oy + effectiveHeadR) - (oy + H / 2.0)) : oy; double xc_right = xInnerRight - Rm + arcHeight; double fx_R_tang = effectiveHeadR > 0 ? xc_right + (Rm / (Rm - effectiveHeadR)) * (xf_right - xc_right) : xInnerRight; double fy_R_tangBot = effectiveHeadR > 0 ? (oy + H / 2.0) + (Rm / (Rm - effectiveHeadR)) * ((oy + effectiveHeadR) - (oy + H / 2.0)) : oy; // 绘制左侧框线(只画横向连接线) DrawBoxSideFrame(ctx, xf_left, xSectionLeft + sectionFilletR, oy, H, 0, isLeft: true); // 绘制右侧框线(只画横向连接线) DrawBoxSideFrame(ctx, xSectionRight - sectionFilletR, xf_right, oy, H, 0, isLeft: false); // 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧及外侧平滑过渡圆角) DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, effectiveHeadR, xf_left, xf_right); // 绘制圆角的斜向标注(如果存在圆角):4-R<=* if (effectiveHeadR > 0) { // 左上角 double yTop = oy + H; double cxLeft = xf_left; double cyLeft = yTop - effectiveHeadR; DrawHeadFilletRadiusLeader(ctx, cxLeft, cyLeft, effectiveHeadR, true); // 根据需求,左右都标注了的话,只保留一个(左上角),去除右上角标注 // 右上角 // double cxRight = xInnerRight - effectiveHeadR; // DrawHeadFilletRadiusLeader(ctx, cxRight, cyLeft, effectiveHeadR, false); } // 3. 剖面填充 - 只在中间区域 // 使用 sectionFilletR if (sectionFilletR > 0.01) { DrawRingSectionHatchWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, sectionFilletR); } 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) - 位于底部 // 标注从锻件最左端到最右端 (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 = FeatureDrivenDrawer.BuildDimensionText(FeatureDrivenDrawer.FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方,用括号包裹 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FeatureDrivenDrawer.FormatDimNumber(valPrime.Value, null)})"; } // 修正:标注需涵盖内框宽度 (矩形部分) // 修正:引线起点改为内框边界 (xInnerLeft/Right) 和高度底部上移圆角半径,确保闭合到白色线框 // 修正:引线起点严格匹配倒角与圆弧相切处的极限点坐标 (fx_L_tang, fy_L_tangBot) AddLinearDim( ctx, new Point3d(fx_L_tang, fy_L_tangBot, 0), new Point3d(fx_R_tang, fy_R_tangBot, 0), new Point3d((fx_L_tang + fx_R_tang) / 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 = FeatureDrivenDrawer.BuildDimensionText(FeatureDrivenDrawer.FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FeatureDrivenDrawer.FormatDimNumber(valPrime.Value, null)})"; } // 标注从圆弧最外侧开始(避免与圆弧轮廓线重叠) double xOuterRight = ox + visualTotalW; // 修正:标注界线起点应从圆弧顶端开始,往内缩进一个圆弧高度 arcHeight + headFilletR,定位在弦线上 double xDimOrigin = xOuterRight - (arcHeight + headFilletR); 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 = FeatureDrivenDrawer.BuildDimensionText(FeatureDrivenDrawer.FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr); // 添加零件尺寸 (Prime) 到下方 var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3Prime); if (valPrime.HasValue && valPrime.Value > 0) { dimText += $"\\X({FeatureDrivenDrawer.FormatDimNumber(valPrime.Value, null)})"; } // 修正:引线向下延伸 sectionFilletR,以接触直边 // sectionFilletR 是剖面处的圆角半径 double dimYStart = oy + H - sectionFilletR; AddLinearDim( ctx, new Point3d(xSectionLeft, dimYStart, 0), new Point3d(xSectionRight, dimYStart, 0), new Point3d((xSectionLeft + xSectionRight) / 2, oy + H + 15, 0), 0, dimText); } } // 9. 硬度符号 - 从剖面右侧引出 var hardnessVal = bag.GetString(FeatureDrivenDrawer.KeyHardness); if (!string.IsNullOrWhiteSpace(hardnessVal) && hardnessVal != "空") { double xStart = xSectionRight; double yStart = oy + H * 0.25; FeatureDrivenDrawer.DrawHardnessSymbol(ctx, xStart, yStart, hardnessVal); } // 10. 标刻内容引线 - 从剖面左侧引出 var markingText = bag.GetString(FeatureDrivenDrawer.KeyMarkingContent); if (!string.IsNullOrWhiteSpace(markingText)) { double xTarget = xSectionLeft; double yTarget = oy + H * 0.75; DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText); } } private static void DrawBlockRoundHeadOutline(FeatureDrivenDrawer.DrawingContext ctx, double ox, double oy, double totalWidth, double height, double arcHeight, double innerFilletR, double xf_left, double xf_right) { try { double H = height; double r = innerFilletR; double w = arcHeight; if (w < 0.1) return; double Rm = (H * H) / (8.0 * w) + w / 2.0; // Left side double xInnerLeft = ox + w; double xc_left = xInnerLeft + Rm - w; Point2d C_m_L = new Point2d(xc_left, oy + H / 2.0); Point2d C_fTop_L = new Point2d(xf_left, oy + H - r); Point2d C_fBot_L = new Point2d(xf_left, oy + r); double fx_L_tangTop = r > 0 ? C_m_L.X + (Rm / (Rm - r)) * (C_fTop_L.X - C_m_L.X) : xInnerLeft; double fy_L_tangTop = r > 0 ? C_m_L.Y + (Rm / (Rm - r)) * (C_fTop_L.Y - C_m_L.Y) : oy + H; double fx_L_tangBot = r > 0 ? C_m_L.X + (Rm / (Rm - r)) * (C_fBot_L.X - C_m_L.X) : xInnerLeft; double fy_L_tangBot = r > 0 ? C_m_L.Y + (Rm / (Rm - r)) * (C_fBot_L.Y - C_m_L.Y) : oy; var leftPoly = new Polyline(); if (r > 0.01) { double angTop1 = Math.PI / 2.0; double angTop2 = Math.Atan2(fy_L_tangTop - C_fTop_L.Y, fx_L_tangTop - C_fTop_L.X); double diffTop = angTop2 - angTop1; while (diffTop < 0) diffTop += Math.PI * 2; while (diffTop >= Math.PI * 2) diffTop -= Math.PI * 2; double bulgeTop = Math.Tan(diffTop / 4.0); double angM1 = Math.Atan2(fy_L_tangTop - C_m_L.Y, fx_L_tangTop - C_m_L.X); double angM2 = Math.Atan2(fy_L_tangBot - C_m_L.Y, fx_L_tangBot - C_m_L.X); double diffM = angM2 - angM1; while (diffM < 0) diffM += Math.PI * 2; while (diffM >= Math.PI * 2) diffM -= Math.PI * 2; double bulgeM = Math.Tan(diffM / 4.0); double angBot1 = Math.Atan2(fy_L_tangBot - C_fBot_L.Y, fx_L_tangBot - C_fBot_L.X); double angBot2 = -Math.PI / 2.0; double diffBot = angBot2 - angBot1; while (diffBot < 0) diffBot += Math.PI * 2; while (diffBot >= Math.PI * 2) diffBot -= Math.PI * 2; double bulgeBot = Math.Tan(diffBot / 4.0); leftPoly.AddVertexAt(0, new Point2d(xf_left, oy + H), bulgeTop, 0, 0); leftPoly.AddVertexAt(1, new Point2d(fx_L_tangTop, fy_L_tangTop), bulgeM, 0, 0); leftPoly.AddVertexAt(2, new Point2d(fx_L_tangBot, fy_L_tangBot), bulgeBot, 0, 0); leftPoly.AddVertexAt(3, new Point2d(xf_left, oy), 0, 0, 0); } else { double angM1 = Math.Atan2(oy + H - C_m_L.Y, xInnerLeft - C_m_L.X); double angM2 = Math.Atan2(oy - C_m_L.Y, xInnerLeft - C_m_L.X); double diffM = angM2 - angM1; while (diffM < 0) diffM += Math.PI * 2; while (diffM >= Math.PI * 2) diffM -= Math.PI * 2; double bulgeM = Math.Tan(diffM / 4.0); leftPoly.AddVertexAt(0, new Point2d(xInnerLeft, oy + H), bulgeM, 0, 0); leftPoly.AddVertexAt(1, new Point2d(xInnerLeft, oy), 0, 0, 0); } ctx.Style?.Apply(leftPoly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(leftPoly); ctx.Tr.AddNewlyCreatedDBObject(leftPoly, true); var leftClosureLine = new Line(new Point3d(fx_L_tangTop, fy_L_tangTop, 0), new Point3d(fx_L_tangBot, fy_L_tangBot, 0)); ctx.Style?.Apply(leftClosureLine, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(leftClosureLine); ctx.Tr.AddNewlyCreatedDBObject(leftClosureLine, true); // Right side double xInnerRight = ox + totalWidth - w; double xc_right = xInnerRight - Rm + w; Point2d C_m_R = new Point2d(xc_right, oy + H / 2.0); Point2d C_fTop_R = new Point2d(xf_right, oy + H - r); Point2d C_fBot_R = new Point2d(xf_right, oy + r); double fx_R_tangTop = r > 0 ? C_m_R.X + (Rm / (Rm - r)) * (C_fTop_R.X - C_m_R.X) : xInnerRight; double fy_R_tangTop = r > 0 ? C_m_R.Y + (Rm / (Rm - r)) * (C_fTop_R.Y - C_m_R.Y) : oy + H; double fx_R_tangBot = r > 0 ? C_m_R.X + (Rm / (Rm - r)) * (C_fBot_R.X - C_m_R.X) : xInnerRight; double fy_R_tangBot = r > 0 ? C_m_R.Y + (Rm / (Rm - r)) * (C_fBot_R.Y - C_m_R.Y) : oy; var rightPoly = new Polyline(); if (r > 0.01) { double angBot1 = -Math.PI / 2.0; double angBot2 = Math.Atan2(fy_R_tangBot - C_fBot_R.Y, fx_R_tangBot - C_fBot_R.X); double diffBot = angBot2 - angBot1; while (diffBot < 0) diffBot += Math.PI * 2; while (diffBot >= Math.PI * 2) diffBot -= Math.PI * 2; double bulgeBot = Math.Tan(diffBot / 4.0); double angM1 = Math.Atan2(fy_R_tangBot - C_m_R.Y, fx_R_tangBot - C_m_R.X); double angM2 = Math.Atan2(fy_R_tangTop - C_m_R.Y, fx_R_tangTop - C_m_R.X); double diffM = angM2 - angM1; while (diffM < 0) diffM += Math.PI * 2; while (diffM >= Math.PI * 2) diffM -= Math.PI * 2; double bulgeM = Math.Tan(diffM / 4.0); double angTop1 = Math.Atan2(fy_R_tangTop - C_fTop_R.Y, fx_R_tangTop - C_fTop_R.X); double angTop2 = Math.PI / 2.0; double diffTop = angTop2 - angTop1; while (diffTop < 0) diffTop += Math.PI * 2; while (diffTop >= Math.PI * 2) diffTop -= Math.PI * 2; double bulgeTop = Math.Tan(diffTop / 4.0); rightPoly.AddVertexAt(0, new Point2d(xf_right, oy), bulgeBot, 0, 0); rightPoly.AddVertexAt(1, new Point2d(fx_R_tangBot, fy_R_tangBot), bulgeM, 0, 0); rightPoly.AddVertexAt(2, new Point2d(fx_R_tangTop, fy_R_tangTop), bulgeTop, 0, 0); rightPoly.AddVertexAt(3, new Point2d(xf_right, oy + H), 0, 0, 0); } else { double angM1 = Math.Atan2(oy - C_m_R.Y, xInnerRight - C_m_R.X); double angM2 = Math.Atan2(oy + H - C_m_R.Y, xInnerRight - C_m_R.X); double diffM = angM2 - angM1; while (diffM < 0) diffM += Math.PI * 2; while (diffM >= Math.PI * 2) diffM -= Math.PI * 2; double bulgeM = Math.Tan(diffM / 4.0); rightPoly.AddVertexAt(0, new Point2d(xInnerRight, oy), bulgeM, 0, 0); rightPoly.AddVertexAt(1, new Point2d(xInnerRight, oy + H), 0, 0, 0); } ctx.Style?.Apply(rightPoly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(rightPoly); ctx.Tr.AddNewlyCreatedDBObject(rightPoly, true); var rightClosureLine = new Line(new Point3d(fx_R_tangTop, fy_R_tangTop, 0), new Point3d(fx_R_tangBot, fy_R_tangBot, 0)); ctx.Style?.Apply(rightClosureLine, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(rightClosureLine); ctx.Tr.AddNewlyCreatedDBObject(rightClosureLine, 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 topPoly = new Polyline(); topPoly.AddVertexAt(0, new Point2d(xStart, yTop), 0, 0, 0); topPoly.AddVertexAt(1, new Point2d(xEnd, yTop), 0, 0, 0); ctx.Style?.Apply(topPoly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(topPoly); ctx.Tr.AddNewlyCreatedDBObject(topPoly, true); var botPoly = new Polyline(); botPoly.AddVertexAt(0, new Point2d(xStart, yBottom), 0, 0, 0); botPoly.AddVertexAt(1, new Point2d(xEnd, yBottom), 0, 0, 0); ctx.Style?.Apply(botPoly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(botPoly); ctx.Tr.AddNewlyCreatedDBObject(botPoly, true); } private static void DrawRingSectionHatchWithFillet(FeatureDrivenDrawer.DrawingContext ctx, double xInnerRight, double xOuterRight, double yBottom, double height, double filletR) { if (xOuterRight <= xInnerRight) { return; } var availableWidth = xOuterRight - xInnerRight; var maxR = Math.Min(availableWidth * 0.5, height * 0.5); var r = Math.Min(filletR, maxR); if (r <= 0.01) { DrawBlockSectionHatch(ctx, xInnerRight, xOuterRight, yBottom, height); return; } try { var yTop = yBottom + height; var bulge = Math.Tan(Math.PI / 8.0); var boundary = new Polyline(); boundary.AddVertexAt(0, new Point2d(xInnerRight + r, yBottom), 0, 0, 0); boundary.AddVertexAt(1, new Point2d(xOuterRight - r, yBottom), bulge, 0, 0); boundary.AddVertexAt(2, new Point2d(xOuterRight, yBottom + r), 0, 0, 0); boundary.AddVertexAt(3, new Point2d(xOuterRight, yTop - r), bulge, 0, 0); boundary.AddVertexAt(4, new Point2d(xOuterRight - r, yTop), 0, 0, 0); boundary.AddVertexAt(5, new Point2d(xInnerRight + r, yTop), bulge, 0, 0); boundary.AddVertexAt(6, new Point2d(xInnerRight, yTop - r), 0, 0, 0); boundary.AddVertexAt(7, new Point2d(xInnerRight, yBottom + r), bulge, 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); try { boundary.Erase(true); } catch { } } catch { } } private static void DrawBlockSectionHatch(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height) { if (xRight <= xLeft) { return; } try { var boundary = new Polyline(); 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, yBottom + height), 0, 0, 0); boundary.AddVertexAt(3, new Point2d(xLeft, yBottom + height), 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); try { boundary.Erase(true); } catch { } } catch { } } 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 DrawHeadFilletRadiusLeader(FeatureDrivenDrawer.DrawingContext ctx, double cx, double cy, double r, bool isLeftSection) { try { var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxRoundHeadFilletRadiusMax); if (!val.HasValue || val.Value <= 0) return; // 左上角向左上(135),右上角向右上(45) double angleDeg = isLeftSection ? 135 : 45; double angleRad = angleDeg * Math.PI / 180.0; // 起点(外圆角,引线从圆弧表面引出) double px = cx + r * Math.Cos(angleRad); double py = cy + r * Math.Sin(angleRad); // 终点 double ex = px + 25 * Math.Cos(angleRad); double ey = py + 25 * Math.Sin(angleRad); // 画直线 var line = new Line(new Point3d(px, py, 0), new Point3d(ex, ey, 0)); ctx.Style?.Apply(line, DrawingStyleManager.Role.Dimension); line.ColorIndex = 4; // Cyan ctx.Btr.AppendEntity(line); ctx.Tr.AddNewlyCreatedDBObject(line, true); // 绘制实心箭头 double arrowLength = 5.0; // 箭头长度,加长 double arrowWidth = 1.5; // 箭头宽度,加宽 double baseX = px + arrowLength * Math.Cos(angleRad); double baseY = py + arrowLength * Math.Sin(angleRad); double perpAngle = angleRad + Math.PI / 2; double p1X = baseX + (arrowWidth / 2) * Math.Cos(perpAngle); double p1Y = baseY + (arrowWidth / 2) * Math.Sin(perpAngle); double p2X = baseX - (arrowWidth / 2) * Math.Cos(perpAngle); double p2Y = baseY - (arrowWidth / 2) * Math.Sin(perpAngle); var arrow = new Solid(new Point3d(px, py, 0), new Point3d(p1X, p1Y, 0), new Point3d(p2X, p2Y, 0)); arrow.ColorIndex = 4; ctx.Btr.AppendEntity(arrow); ctx.Tr.AddNewlyCreatedDBObject(arrow, true); // 文字(与线对齐) var textStr = $"4-R\\U+2264{val.Value}"; var text = new DBText(); text.TextString = textStr; text.Height = 3.5; text.ColorIndex = 4; // Cyan // 倾斜文字角度 double textRotation = angleRad; if (isLeftSection) { // 左侧角度是 135,文字应该 -45度(减去180)保证从左向右读 textRotation = angleRad - Math.PI; } text.Rotation = textRotation; // 对齐设置 text.HorizontalMode = TextHorizontalMode.TextCenter; text.VerticalMode = TextVerticalMode.TextBottom; // 计算文字放置点,紧贴在线端上方 double offsetX = 2.0 * Math.Cos(textRotation + Math.PI / 2); double offsetY = 2.0 * Math.Sin(textRotation + Math.PI / 2); var tp = new Point3d(ex + offsetX, ey + offsetY, 0); text.AlignmentPoint = tp; text.Position = tp; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); text.ColorIndex = 4; ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { // ignore } } 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 { } } private static void AddLinearDim(FeatureDrivenDrawer.DrawingContext ctx, Point3d pt1, Point3d pt2, Point3d dimLinePt, double rotationDeg, string textOverride, Action customizer = null) { try { var prevDb = HostApplicationServices.WorkingDatabase; HostApplicationServices.WorkingDatabase = ctx.Db; try { double rotRad = rotationDeg * Math.PI / 180.0; var dim = new RotatedDimension(rotRad, pt1, pt2, dimLinePt, textOverride, ctx.Db.Dimstyle); try { dim.SetDatabaseDefaults(); } catch { } dim.ColorIndex = 3; if (!string.IsNullOrEmpty(textOverride)) { dim.DimensionText = textOverride; } try { dim.Normal = Vector3d.ZAxis; } catch { } TryApplyDimSizeOverrides(dim); TryApplyDimLayoutOverrides(dim); customizer?.Invoke(dim, ctx); ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension); dim.ColorIndex = 3; ctx.Btr.AppendEntity(dim); ctx.Tr.AddNewlyCreatedDBObject(dim, true); try { dim.RecomputeDimensionBlock(true); } catch { } } finally { HostApplicationServices.WorkingDatabase = prevDb; } } catch { } } private static void TryApplyDimSizeOverrides(Dimension dim) { if (dim == null) return; TrySetDimProp(dim, "Dimtxt", 3.5); TrySetDimProp(dim, "Dimasz", 2.5); TrySetDimProp(dim, "Dimgap", 1.0); TrySetDimProp(dim, "Dimexe", 1.0); TrySetDimProp(dim, "Dimexo", 0.0); TrySetDimProp(dim, "Dimtofl", true); TrySetDimProp(dim, "Dimatfit", 3); TrySetDimProp(dim, "Dimtad", 1); TrySetDimProp(dim, "Dimclrd", 3); TrySetDimProp(dim, "Dimclre", 3); TrySetDimProp(dim, "Dimclrt", 3); } private static void TryApplyDimLayoutOverrides(Dimension dim) { if (dim == null) return; TrySetDimProp(dim, "Dimtih", false); TrySetDimProp(dim, "Dimtoh", false); TrySetDimProp(dim, "Dimse1", false); TrySetDimProp(dim, "Dimse2", false); } private static void TrySetDimProp(object obj, string propName, double value) { if (obj == null) return; try { var prop = obj.GetType().GetProperty(propName); if (prop != null && prop.CanWrite && prop.PropertyType == typeof(double)) prop.SetValue(obj, value, null); } catch { } } private static void TrySetDimProp(object obj, string propName, bool value) { if (obj == null) return; try { var prop = obj.GetType().GetProperty(propName); if (prop != null && prop.CanWrite && prop.PropertyType == typeof(bool)) prop.SetValue(obj, value, null); } catch { } } private static void TrySetDimProp(object obj, string propName, int value) { if (obj == null) return; try { var prop = obj.GetType().GetProperty(propName); if (prop != null && prop.CanWrite && prop.PropertyType == typeof(int)) prop.SetValue(obj, value, null); } catch { } } private static ParamBag ScaleParamBag(ParamBag original, double scaleFactor) { if (original == null || Math.Abs(scaleFactor - 1.0) < 0.000001) return original; var newBag = new ParamBag(); foreach(var k in original.GetKeys()) { var v = original.GetString(k); if (k.Contains("Diameter") || k.Contains("Length") || k.Contains("Radius") || k.Contains("Prime") || k.Contains("Height") || k.Contains("Thickness") || k.Contains("Size")) { if (double.TryParse(v, out var d)) { newBag.Set(k, (d * scaleFactor).ToString("0.###")); } else { newBag.Set(k, v); } } else { newBag.Set(k, v); } } return newBag; } } }