using System; using System.Collections.Generic; using System.Linq; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using CadParamPluging.Common; namespace CadParamPluging.Cad { /// /// 特征驱动绘图引擎 - 根据参数存在性动态绘制图形 /// public static class FeatureDrivenDrawer { #region 参数Key常量 // 环形参数 public const string KeyOuterDiameter1 = "OuterDiameter1"; public const string KeyOuterDiameter1TolPlus = "OuterDiameter1TolPlus"; public const string KeyOuterDiameter1TolMinus = "OuterDiameter1TolMinus"; public const string KeyInnerDiameter2 = "InnerDiameter2"; public const string KeyInnerDiameter2TolPlus = "InnerDiameter2TolPlus"; public const string KeyInnerDiameter2TolMinus = "InnerDiameter2TolMinus"; public const string KeyHeight1 = "Height1"; public const string KeyHeight1TolPlus = "Height1TolPlus"; public const string KeyHeight1TolMinus = "Height1TolMinus"; public const string KeyMinWallThickness = "MinWallThickness"; public const string KeyUnspecifiedFilletRadiusMax = "UnspecifiedFilletRadiusMax"; // 环形未注圆角 public const string KeyInnerRadiusMax = "InnerRadiusMax"; // 内径半径R≤(中心冲孔) // 环形零件尺寸(车加工态) public const string KeyOuterDiameter1Prime = "OuterDiameter1Prime"; public const string KeyInnerDiameter2Prime = "InnerDiameter2Prime"; public const string KeyHeight1Prime = "Height1Prime"; // 饼盘参数 public const string KeyDiskDiameter = "DiskDiameter"; public const string KeyDiskDiameterTolPlus = "DiskDiameterTolPlus"; public const string KeyDiskDiameterTolMinus = "DiskDiameterTolMinus"; public const string KeyDiskHeight = "DiskHeight"; public const string KeyDiskHeightTolPlus = "DiskHeightTolPlus"; public const string KeyDiskHeightTolMinus = "DiskHeightTolMinus"; public const string KeyDiskFilletRadiusMax = "DiskFilletRadiusMax"; // 饼盘未注圆角 public const string KeyDiskDiameterPrime = "DiskDiameterPrime"; // 饼盘零件直径 public const string KeyDiskHeightPrime = "DiskHeightPrime"; // 饼盘零件高度 // 轴杆参数(圆轴) public const string KeyDiameter = "Diameter"; public const string KeyDiameterTolPlus = "DiameterTolPlus"; public const string KeyDiameterTolMinus = "DiameterTolMinus"; public const string KeyLength = "Length"; public const string KeyLengthTolPlus = "LengthTolPlus"; public const string KeyLengthTolMinus = "LengthTolMinus"; public const string KeyShaftFilletRadiusMax = "ShaftFilletRadiusMax"; // 轴杆未注圆角 public const string KeyDiameterPrime = "DiameterPrime"; // 轴杆零件直径 public const string KeyLengthPrime = "LengthPrime"; // 轴杆零件长度 // 方体参数 public const string KeyBoxSize1 = "BoxSize1"; public const string KeyBoxSize1TolPlus = "BoxSize1TolPlus"; public const string KeyBoxSize1TolMinus = "BoxSize1TolMinus"; public const string KeyBoxSize2 = "BoxSize2"; public const string KeyBoxSize2TolPlus = "BoxSize2TolPlus"; public const string KeyBoxSize2TolMinus = "BoxSize2TolMinus"; public const string KeyBoxSize3 = "BoxSize3"; public const string KeyBoxSize3TolPlus = "BoxSize3TolPlus"; public const string KeyBoxSize3TolMinus = "BoxSize3TolMinus"; public const string KeyBoxFilletRadiusMax = "BoxFilletRadiusMax"; // 方体未注圆角 public const string KeyBoxRoundHeadFilletRadiusMax = "BoxRoundHeadFilletRadiusMax"; // 方体圆头处圆角 public const string KeyBoxSize1Prime = "BoxSize1Prime"; // 方体零件尺寸1 public const string KeyBoxSize2Prime = "BoxSize2Prime"; // 方体零件尺寸2 public const string KeyBoxSize3Prime = "BoxSize3Prime"; // 方体零件尺寸3 #endregion #region 绘图上下文 public class DrawingContext { public CadContext Ctx { get; set; } public ParamBag Bag { get; set; } public ParamBag OriginalBag { get; set; } public Point3d Center { get; set; } public string DeliveryStatus { get; set; } public string StructuralFeature { get; set; } public string SpecialCondition { get; set; } public string ProcessMethod { get; set; } internal DrawingStyleManager Style { get; set; } public Database Db => Ctx?.Database; public Transaction Tr => Ctx?.Transaction; public BlockTableRecord Btr { get; set; } public bool IsMachined => !string.IsNullOrWhiteSpace(DeliveryStatus) && DeliveryStatus.IndexOf("车加工", StringComparison.OrdinalIgnoreCase) >= 0; public bool IsCenterPunched => !string.IsNullOrWhiteSpace(SpecialCondition) && SpecialCondition.IndexOf("中心冲孔", StringComparison.OrdinalIgnoreCase) >= 0; public bool HasRoundHead => !string.IsNullOrWhiteSpace(SpecialCondition) && SpecialCondition.IndexOf("有圆头", StringComparison.OrdinalIgnoreCase) >= 0; /// /// 是否为轧制工艺(轧制时只画右半边图) /// public bool IsRolling => !string.IsNullOrWhiteSpace(ProcessMethod) && ProcessMethod.IndexOf("轧制", StringComparison.OrdinalIgnoreCase) >= 0; } #endregion #region 预期图形尺寸计算 /// /// 预期图形尺寸 /// public class ExpectedDrawingSize { public double Width { get; set; } public double Height { get; set; } public bool IsValid => Width > 0 && Height > 0; } /// /// 根据参数计算预期绘制图形的尺寸(不包含标注,仅计算图形主体) /// public static ExpectedDrawingSize CalculateExpectedSize(ParamBag bag, string structuralFeature) { if (bag == null) { return new ExpectedDrawingSize(); } if (IsRing(structuralFeature)) { return CalculateRingSize(bag); } if (IsDisk(structuralFeature)) { return CalculateDiskSize(bag); } if (IsShaft(structuralFeature)) { return CalculateShaftSize(bag); } if (IsBlock(structuralFeature)) { return CalculateBlockSize(bag); } return new ExpectedDrawingSize(); } private static ExpectedDrawingSize CalculateRingSize(ParamBag bag) { var outerDia = bag.GetDoubleOrNull(KeyOuterDiameter1) ?? 0; var height = bag.GetDoubleOrNull(KeyHeight1) ?? 0; // Check if schematic mode applies (Machined + Rolling) // Need to fetch from bag as we don't have DrawingContext here var deliveryStatus = bag.GetString("DeliveryStatus"); var processMethod = bag.GetString("ProcessMethod"); bool isMachined = !string.IsNullOrWhiteSpace(deliveryStatus) && deliveryStatus.Contains("车加工"); bool isRolling = !string.IsNullOrWhiteSpace(processMethod) && processMethod.Contains("轧制"); double activeWidth = outerDia; double activeHeight = height; var extraMargin = 50.0; // 标注和文字的默认边距(双侧共100) if (isMachined && isRolling) { var minWallThickness = bag.GetDoubleOrNull(KeyMinWallThickness); if (minWallThickness.HasValue && minWallThickness.Value > 0) { // 示意图模式:视觉宽度 = 最小壁厚 * 3(不是物理外径) // 比例 2:1 内孔:实体 activeWidth = minWallThickness.Value * 3.0; } else { // 降级:视觉宽度 = 高度 * 3 activeWidth = height * 3.0; } // 示意图模式下,图形较小,标注边距也相应调整 // 但不要过大,否则会错误触发"超出边框"检测 extraMargin = 60.0; // 示意图的边距稍大于默认值 } // 环形图形:宽度=视觉宽度(X方向),高度=高度参数(Y方向) // 加上标注的额外空间(双侧) return new ExpectedDrawingSize { Width = activeWidth + extraMargin * 2, Height = activeHeight + extraMargin * 2 }; } private static ExpectedDrawingSize CalculateDiskSize(ParamBag bag) { var diameter = bag.GetDoubleOrNull(KeyDiskDiameter) ?? 0; var height = bag.GetDoubleOrNull(KeyDiskHeight) ?? 0; var extraMargin = 40; return new ExpectedDrawingSize { Width = diameter + extraMargin * 2, Height = height + extraMargin * 2 }; } private static ExpectedDrawingSize CalculateShaftSize(ParamBag bag) { var diameter = bag.GetDoubleOrNull(KeyDiameter) ?? 0; var length = bag.GetDoubleOrNull(KeyLength) ?? 0; var extraMargin = 40; return new ExpectedDrawingSize { Width = length + extraMargin * 2, Height = diameter + extraMargin * 2 }; } private static ExpectedDrawingSize CalculateBlockSize(ParamBag bag) { var size1 = bag.GetDoubleOrNull(KeyBoxSize1) ?? 0; var size2 = bag.GetDoubleOrNull(KeyBoxSize2) ?? size1; var extraMargin = 40; return new ExpectedDrawingSize { Width = size1 + extraMargin * 2, Height = size2 + extraMargin * 2 }; } #endregion #region 主入口 /// /// 绘制图形(带缩放支持) /// /// CAD上下文 /// 参数包 /// 交付状态 /// 结构特征 /// 特殊条件 /// 工艺方法(轧制/自由锻) /// 绘图中心点 /// 缩放比例(1.0表示不缩放) public static void Draw(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature, string specialCondition = null, string processMethod = null, Point3d? center = null, 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; // Important: templates & cleanup operate on ModelSpace; draw there to avoid PaperSpace-related dimension issues. 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 DrawingContext { Ctx = ctx, Bag = effectiveBag, OriginalBag = bag, Center = center ?? Point3d.Origin, DeliveryStatus = deliveryStatus, StructuralFeature = structuralFeature, SpecialCondition = specialCondition, ProcessMethod = processMethod, Btr = btr, Style = new DrawingStyleManager(db, tr) }; // 根据结构特征分发到对应绘制器 if (IsRing(structuralFeature)) { DrawRingFeatures(context); } else if (IsDisk(structuralFeature)) { DrawDiskFeatures(context); } else if (IsShaft(structuralFeature)) { DrawShaftFeatures(context); } else if (IsBlock(structuralFeature)) { DrawBlockFeatures(context); } } #endregion #region 结构特征判断 private static bool IsRing(string feature) { return !string.IsNullOrWhiteSpace(feature) && feature.IndexOf("环形", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsDisk(string feature) { return !string.IsNullOrWhiteSpace(feature) && (feature.IndexOf("饼盘", StringComparison.OrdinalIgnoreCase) >= 0 || feature.IndexOf("饼", StringComparison.OrdinalIgnoreCase) >= 0 || feature.IndexOf("盘", StringComparison.OrdinalIgnoreCase) >= 0); } private static bool IsShaft(string feature) { return !string.IsNullOrWhiteSpace(feature) && (feature.IndexOf("轴杆", StringComparison.OrdinalIgnoreCase) >= 0 || feature.IndexOf("轴", StringComparison.OrdinalIgnoreCase) >= 0 || feature.IndexOf("杆", StringComparison.OrdinalIgnoreCase) >= 0); } private static bool IsBlock(string feature) { return !string.IsNullOrWhiteSpace(feature) && feature.IndexOf("方体", StringComparison.OrdinalIgnoreCase) >= 0; } #endregion #region 环形绘制 - 特征驱动 private static void DrawRingFeatures(DrawingContext ctx) { var bag = ctx.Bag; // 必需参数检测 var outerDia = bag.GetDoubleOrNull(KeyOuterDiameter1); var height = bag.GetDoubleOrNull(KeyHeight1); if (!outerDia.HasValue || outerDia.Value <= 0) { return; // 缺少外径,无法绘制 } if (!height.HasValue || height.Value <= 0) { return; // 缺少高度,无法绘制 } // 判断是轧制还是自由锻 if (ctx.IsRolling) { // 轧制:只画右半边图 DrawRingFeaturesHalf(ctx, outerDia.Value, height.Value); } else { // 自由锻:画全图 DrawRingFeaturesFull(ctx, outerDia.Value, height.Value); } } /// /// 轧制工艺 - 只画右半边图 /// private static void DrawRingFeaturesHalf(DrawingContext ctx, double outerDia, double height) { var bag = ctx.Bag; // --- 几何与视觉映射计算 --- // --- 几何与视觉映射计算 --- double H = height; double physicalOuterR = outerDia / 2.0; double physicalInnerR = 0; var innerDia = bag.GetDoubleOrNull(KeyInnerDiameter2); if (innerDia.HasValue && innerDia.Value > 0) { physicalInnerR = innerDia.Value / 2.0; } // 1. 确定视觉半径 // 默认情况:等于物理半径 double visualOuterR = physicalOuterR; double visualInnerR = physicalInnerR; // 轧制+车加工(示意图模式)特殊处理 if (ctx.IsMachined) { var minWallThkParam = bag.GetDoubleOrNull(KeyMinWallThickness); if (minWallThkParam.HasValue && minWallThkParam.Value > 0) { // 用户新需求:比例改为 3:1 // 指定规则:图纸总宽度(视觉外半径) = 最小壁厚 * 3 // 这意味着: // 视觉内半径 (孔) = 最小壁厚 * 2 (占2份) // 视觉剖面宽度 (壁) = 最小壁厚 * 1 (占1份) // 从而实现了 2:1 的内孔/实体分割 visualInnerR = minWallThkParam.Value * 2.0; visualOuterR = minWallThkParam.Value * 3.0; } else { // 降级策略:维持 3:1 比例 visualOuterR = H * 3.0; visualInnerR = visualOuterR * (2.0/3.0); } } // 纯轧制模式(非车加工):按物理比例绘制,无需特殊处理 else if (Math.Abs(physicalOuterR) > 1e-6) // 避免除零 { // visualInnerR 已经在上面初始化为 physicalInnerR // 这里不需要改变,因为 visualOuterR = physicalOuterR } // 3. 定义半径映射函数 (非线性映射:孔区线性,壁厚区线性) // r: 物理半径 double MapRadius(double r) { if (r <= 0) return 0; // 如果没有内孔,直接线性映射 if (physicalInnerR <= 1e-6) { return r * (visualOuterR / physicalOuterR); } if (r <= physicalInnerR) { // 孔区映射 return r * (visualInnerR / physicalInnerR); } else { // 壁厚区映射 // V = Vir + (r - Pir) * (Vor - Vir) / (Por - Pir) double wallScale = (visualOuterR - visualInnerR) / (physicalOuterR - physicalInnerR); return visualInnerR + (r - physicalInnerR) * wallScale; } } // --- 绘图坐标计算 --- // 轧制时图形只画右半边,但要保持居中 double ox = ctx.Center.X - visualOuterR / 2.0; // 对称轴位置(视觉左边界) double oy = ctx.Center.Y - H / 2.0; // 车加工态零件尺寸 double? outerDiaPrime = null; double? innerDiaPrime = null; double? heightPrime = null; if (ctx.IsMachined) { outerDiaPrime = bag.GetDoubleOrNull(KeyOuterDiameter1Prime); innerDiaPrime = bag.GetDoubleOrNull(KeyInnerDiameter2Prime); heightPrime = bag.GetDoubleOrNull(KeyHeight1Prime); } // === 特征1: 锻件外轮廓 === DrawForgingOuterContourHalf(ctx, ox, oy, visualOuterR, H); // === 特征1.1: 对称轴 === DrawRingSymmetryAxis(ctx, ox, oy, H); // === 特征2: 外径公差标注 === var outerTolPlus = bag.GetDoubleOrNull(KeyOuterDiameter1TolPlus); var outerTolMinus = bag.GetDoubleOrNull(KeyOuterDiameter1TolMinus); // 传入 visualOuterR 用于画线,传入 outerDia 用于文本 DrawOuterDiameterDimensionHalf(ctx, ox, oy, visualOuterR, H, outerDia, outerTolPlus, outerTolMinus, outerDiaPrime); // === 特征3: 内孔/内径 === double? xInnerRight = null; if (innerDia.HasValue && innerDia.Value > 0 && innerDia.Value < outerDia) { // 计算视觉上的内径右边界 xInnerRight = ox + visualInnerR; var innerTolPlus = bag.GetDoubleOrNull(KeyInnerDiameter2TolPlus); var innerTolMinus = bag.GetDoubleOrNull(KeyInnerDiameter2TolMinus); DrawInnerHoleHalf(ctx, ox, xInnerRight.Value, oy, H); // 传入 raw innerDia for text DrawInnerDiameterDimensionHalf(ctx, ox, xInnerRight.Value, oy, H, innerDia.Value, innerTolPlus, innerTolMinus, innerDiaPrime); // === 特征4: 内径圆角 === var innerRadiusMax = bag.GetDoubleOrNull(KeyInnerRadiusMax); if (innerRadiusMax.HasValue && innerRadiusMax.Value > 0 && ctx.IsCenterPunched) { DrawInnerFilletsHalf(ctx, xInnerRight.Value, oy, H, innerRadiusMax.Value); } // === 特征4.1: 剖面线 === DrawRingSectionHatch(ctx, xInnerRight.Value, ox + visualOuterR, oy, H); // 剖面左侧竖线 DrawSectionInnerBoundaryBold(ctx, xInnerRight.Value, oy, H); } // === 特征5: 高度标注 === var heightTolPlus = bag.GetDoubleOrNull(KeyHeight1TolPlus); var heightTolMinus = bag.GetDoubleOrNull(KeyHeight1TolMinus); DrawHeightDimensionHalf(ctx, ox, oy, visualOuterR, H, heightTolPlus, heightTolMinus, heightPrime); // === 特征6: 未注圆角 === var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { DrawUnspecifiedFilletNote(ctx, ox, oy, H, visualOuterR, unspecifiedFillet.Value); } // === 特征7: 最小壁厚min标注 === var minWallThickness = bag.GetDoubleOrNull(KeyMinWallThickness); if (minWallThickness.HasValue && minWallThickness.Value > 0 && xInnerRight.HasValue) { DrawMinWallThicknessNote(ctx, xInnerRight.Value, ox + visualOuterR, oy, minWallThickness.Value); } // === 特征8: 零件轮廓(车加工态,右半边) === if (ctx.IsMachined && outerDiaPrime.HasValue && outerDiaPrime.Value > 0 && heightPrime.HasValue && heightPrime.Value > 0) { // 零件轮廓(黄色)尺寸计算: 基于实际尺寸差值(间隙)进行映射 // 1. 外径 (Mapped) double physicalPartOuterR = outerDiaPrime.Value / 2.0; double visualPartOuterR = MapRadius(physicalPartOuterR); var scaledOuterDiaPrime = visualPartOuterR * 2.0; // 2. 内径 (Mapped) double? scaledInnerDiaPrime = null; if (innerDiaPrime.HasValue && innerDiaPrime.Value > 0) { double physicalPartInnerR = innerDiaPrime.Value / 2.0; double visualPartInnerR = MapRadius(physicalPartInnerR); scaledInnerDiaPrime = visualPartInnerR * 2.0; } // 3. 高度 (Direct, Y-axis is 1:1) // 高度方向没有示意图扭曲,直接使用物理高度 var scaledHeightPrime = heightPrime.Value; DrawPartContourHalf(ctx, ox, oy, H, scaledOuterDiaPrime, scaledInnerDiaPrime, scaledHeightPrime); } // === 特征10: 轧制+车加工 特殊引线标注 (HB5936-13) === // 需满足: 车加工 + 轧制(本方法即为轧制) + 有内孔 if (ctx.IsMachined && innerDia.HasValue && innerDia.Value > 0) { // 指向锻件内孔右上角(白色线框) var scaledInnerDia = visualInnerR * 2.0; DrawSpecialInnerHoleLeader(ctx, ox, oy, H, scaledInnerDia); } // === 特征9: 硬度符号 === } /// /// 自由锻工艺 - 画全图 /// private static void DrawRingFeaturesFull(DrawingContext ctx, double outerDia, double height) { var bag = ctx.Bag; double W = outerDia; double H = height; double ox = ctx.Center.X - W / 2.0; double oy = ctx.Center.Y - H / 2.0; // 车加工态零件尺寸(用于括号尺寸与零件轮廓) double? outerDiaPrime = null; double? innerDiaPrime = null; double? heightPrime = null; if (ctx.IsMachined) { outerDiaPrime = bag.GetDoubleOrNull(KeyOuterDiameter1Prime); innerDiaPrime = bag.GetDoubleOrNull(KeyInnerDiameter2Prime); heightPrime = bag.GetDoubleOrNull(KeyHeight1Prime); } // === 特征1: 锻件外轮廓(必绘) === DrawForgingOuterContour(ctx, ox, oy, W, H); // === 特征1.1: 中心线 === DrawRingCenterline(ctx, ox, oy, W, H); // === 特征2: 外径公差标注(如果有公差参数) === var outerTolPlus = bag.GetDoubleOrNull(KeyOuterDiameter1TolPlus); var outerTolMinus = bag.GetDoubleOrNull(KeyOuterDiameter1TolMinus); DrawOuterDiameterDimension(ctx, ox, oy, W, outerDia, outerTolPlus, outerTolMinus, outerDiaPrime); // === 特征3: 内孔/内径(如果有内径参数) === var innerDia = bag.GetDoubleOrNull(KeyInnerDiameter2); double? xInnerLeft = null; double? xInnerRight = null; if (innerDia.HasValue && innerDia.Value > 0 && innerDia.Value < W) { xInnerLeft = ctx.Center.X - innerDia.Value / 2.0; xInnerRight = ctx.Center.X + innerDia.Value / 2.0; var innerTolPlus = bag.GetDoubleOrNull(KeyInnerDiameter2TolPlus); var innerTolMinus = bag.GetDoubleOrNull(KeyInnerDiameter2TolMinus); DrawInnerHole(ctx, xInnerLeft.Value, xInnerRight.Value, oy, H); DrawInnerDiameterDimension(ctx, xInnerLeft.Value, xInnerRight.Value, oy, innerDia.Value, innerTolPlus, innerTolMinus, innerDiaPrime); // === 特征4: 内径圆角(如果有内径半径R参数,且是中心冲孔) === var innerRadiusMax = bag.GetDoubleOrNull(KeyInnerRadiusMax); if (innerRadiusMax.HasValue && innerRadiusMax.Value > 0 && ctx.IsCenterPunched) { DrawInnerFillets(ctx, xInnerLeft.Value, xInnerRight.Value, oy, H, innerRadiusMax.Value); } // === 特征4.1: 剖面线(仅剖切端部区域) === DrawRingSectionHatch(ctx, xInnerRight.Value, ox + W, oy, H); // 剖面左侧竖线应为粗实线(剖切边界) DrawSectionInnerBoundaryBold(ctx, xInnerRight.Value, oy, H); } // === 特征5: 高度标注 === var heightTolPlus = bag.GetDoubleOrNull(KeyHeight1TolPlus); var heightTolMinus = bag.GetDoubleOrNull(KeyHeight1TolMinus); DrawHeightDimension(ctx, ox, oy, W, H, heightTolPlus, heightTolMinus, heightPrime); // === 特征6: 未注圆角(如果有该参数) === var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { // 复用现有位置逻辑:右上角 DrawUnspecifiedFilletNote(ctx, ox, oy, H, W, unspecifiedFillet.Value); } // === 特征7: 最小壁厚min标注(如果有该参数) === var minWallThickness = bag.GetDoubleOrNull(KeyMinWallThickness); if (minWallThickness.HasValue && minWallThickness.Value > 0 && xInnerRight.HasValue) { DrawMinWallThicknessNote(ctx, xInnerRight.Value, ox + W, oy, minWallThickness.Value); } // === 特征8: 零件轮廓(车加工态,双点划线) === if (ctx.IsMachined && outerDiaPrime.HasValue && outerDiaPrime.Value > 0 && heightPrime.HasValue && heightPrime.Value > 0) { DrawPartContour(ctx, ctx.Center, outerDiaPrime.Value, innerDiaPrime, heightPrime.Value); } // === 特征9: 硬度符号(有要求才画) === } private static void DrawSectionInnerBoundaryBold(DrawingContext ctx, double x, double yBottom, double height) { try { var line = new Line(new Point3d(x, yBottom, 0), new Point3d(x, yBottom + height, 0)); ctx.Style?.Apply(line, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(line); ctx.Tr.AddNewlyCreatedDBObject(line, true); } catch { // ignore } } private static void DrawForgingOuterContour(DrawingContext ctx, double ox, double oy, double width, double height) { var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0); poly.AddVertexAt(1, new Point2d(ox + width, oy), 0, 0, 0); poly.AddVertexAt(2, new Point2d(ox + width, oy + height), 0, 0, 0); poly.AddVertexAt(3, new Point2d(ox, oy + height), 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 DrawRingCenterline(DrawingContext ctx, double ox, double oy, double width, double height) { var cy = oy + height / 2.0; var line = new Line(new Point3d(ox - 10, cy, 0), new Point3d(ox + width + 10, cy, 0)); ctx.Style?.Apply(line, DrawingStyleManager.Role.Centerline); ctx.Btr.AppendEntity(line); ctx.Tr.AddNewlyCreatedDBObject(line, true); } #region 轧制专用绘图方法(只画右半边) /// /// 轧制:绘制右半边外轮廓(开口多段线) /// private static void DrawForgingOuterContourHalf(DrawingContext ctx, double ox, double oy, double radius, double height) { // 右半边轮廓:下边、右边、上边(左边开口) var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0); // 左下(对称轴下端) poly.AddVertexAt(1, new Point2d(ox + radius, oy), 0, 0, 0); // 右下 poly.AddVertexAt(2, new Point2d(ox + radius, oy + height), 0, 0, 0); // 右上 poly.AddVertexAt(3, new Point2d(ox, oy + height), 0, 0, 0); // 左上(对称轴上端) poly.Closed = false; // 不闭合,左边开口 ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); } /// /// 轧制:绘制截断线(两根双点划线,间距5mm) /// private static void DrawRingSymmetryAxis(DrawingContext ctx, double ox, double oy, double height) { const double extend = 5.0; // 截断线在轮廓上下各延长一点 const double frameMargin = 5.0; // 避免越过图框边界 const double lineSpacing = 3.0; // 两根截断线间距3mm var y0 = oy - extend; var y1 = oy + height + extend; // 若能检测到白色外框,则把截断线端点夹在图框内(留出少量安全边距)。 try { var frameExtents = TemplateDrawingService.ComputeWhiteFrameExtents(ctx?.Ctx); if (frameExtents.HasValue) { var frame = frameExtents.Value; y0 = Math.Max(y0, frame.MinPoint.Y + frameMargin); y1 = Math.Min(y1, frame.MaxPoint.Y - frameMargin); } } catch { // ignore } // 极端情况下(图框过小/检测异常导致端点反转)退回到原始高度。 if (y1 <= y0) { y0 = oy; y1 = oy + height; } // 绘制两根双点划线,间距 lineSpacing // 第一根在 ox 位置 var line1 = new Line(new Point3d(ox, y0, 0), new Point3d(ox, y1, 0)); ctx.Style?.Apply(line1, DrawingStyleManager.Role.BreakLine); // 使用截断线样式(白色双点划线) ctx.Btr.AppendEntity(line1); ctx.Tr.AddNewlyCreatedDBObject(line1, true); // 第二根在 ox - lineSpacing 位置 var line2 = new Line(new Point3d(ox - lineSpacing, y0, 0), new Point3d(ox - lineSpacing, y1, 0)); ctx.Style?.Apply(line2, DrawingStyleManager.Role.BreakLine); // 使用截断线样式(白色双点划线) ctx.Btr.AppendEntity(line2); ctx.Tr.AddNewlyCreatedDBObject(line2, true); } /// /// 轧制:外径标注(从对称轴到右边,标注在图形下方偏内) /// private static void DrawOuterDiameterDimensionHalf(DrawingContext ctx, double ox, double oy, double radius, double height, double diameter, double? tolPlus, double? tolMinus, double? diameterPrime) { // Use OriginalBag for text display to avoid scaled values var diameterVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1) ?? diameter; var diameterStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1TolMinus); var diameterPrimeVal = diameterPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyOuterDiameter1Prime); if (p.HasValue) diameterPrimeVal = p.Value; } var baseText = BuildDimensionText($"%%c{FormatDimNumber(diameterVal, diameterStr)}", tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = diameterPrimeVal.HasValue && diameterPrimeVal.Value > 0 ? baseText + $"\\X(%%c{FormatDimNumber(diameterPrimeVal.Value)})" : baseText; // 标注放在图形底部稍下方 AddLinearDim( ctx, new Point3d(ox, oy, 0), new Point3d(ox + radius, oy, 0), new Point3d(ox + radius / 2, oy - 25, 0), 0, dimText, ApplyHalfSideDimStyle); } /// /// 轧制:只画右侧内孔线 /// private static void DrawInnerHoleHalf(DrawingContext ctx, double oxAxis, double xInnerRight, double yBottom, double height) { var yTop = yBottom + height; // 只画右侧内孔竖线 var lineRight = new Line(new Point3d(xInnerRight, yBottom, 0), new Point3d(xInnerRight, yTop, 0)); ctx.Style?.Apply(lineRight, DrawingStyleManager.Role.Hidden); ctx.Btr.AppendEntity(lineRight); ctx.Tr.AddNewlyCreatedDBObject(lineRight, true); } /// /// 轧制:内径标注(从对称轴到右边内孔,放到下方并与最小壁厚标注共线) /// private static void DrawInnerDiameterDimensionHalf(DrawingContext ctx, double oxAxis, double xInnerRight, double yBottom, double height, double diameter, double? tolPlus, double? tolMinus, double? diameterPrime) { // Use OriginalBag for text display var diameterVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2) ?? diameter; var diameterStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2TolMinus); var diameterPrimeVal = diameterPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyInnerDiameter2Prime); if (p.HasValue) diameterPrimeVal = p.Value; } var baseText = BuildDimensionText($"%%c{FormatDimNumber(diameterVal, diameterStr)}", tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = diameterPrimeVal.HasValue && diameterPrimeVal.Value > 0 ? baseText + $"\\X(%%c{FormatDimNumber(diameterPrimeVal.Value)})" : baseText; double innerRadius = xInnerRight - oxAxis; // 放到下方,并与最小壁厚(30min)同一条水平尺寸线(yBottom - 10) var dimLineY = yBottom - 10; AddLinearDim( ctx, new Point3d(oxAxis, yBottom, 0), new Point3d(xInnerRight, yBottom, 0), new Point3d(oxAxis + innerRadius / 2, dimLineY, 0), 0, dimText, ApplyHalfSideDimStyle); } /// /// 轧制:只画右侧内径圆角 /// private static void DrawInnerFilletsHalf(DrawingContext ctx, double xInnerRight, double yBottom, double height, double filletR) { var yTop = yBottom + height; // 只画右侧两个圆角 // 右上角 DrawFilletArc(ctx, xInnerRight - filletR, yTop - filletR, filletR, 0, Math.PI / 2); // 右下角 DrawFilletArc(ctx, xInnerRight - filletR, yBottom + filletR, filletR, Math.PI * 1.5, Math.PI * 2); } /// /// 轧制:高度标注(在右侧) /// private static void DrawHeightDimensionHalf(DrawingContext ctx, double ox, double oy, double radius, double height, double? tolPlus, double? tolMinus, double? heightPrime) { // Use OriginalBag for text display var heightVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1) ?? height; var heightStr = ctx.OriginalBag?.GetString(KeyHeight1); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyHeight1TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyHeight1TolMinus); var heightPrimeVal = heightPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyHeight1Prime); if (p.HasValue) heightPrimeVal = p.Value; } var baseText = BuildDimensionText(FormatDimNumber(heightVal, heightStr), tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = heightPrimeVal.HasValue && heightPrimeVal.Value > 0 ? baseText + $"\\X({FormatDimNumber(heightPrimeVal.Value)})" : baseText; AddLinearDim( ctx, new Point3d(ox + radius, oy, 0), new Point3d(ox + radius, oy + height, 0), new Point3d(ox + radius + 20, oy + height / 2, 0), 90, dimText); } /// /// 轧制:零件轮廓(闭合方框,双点划线黄色) /// private static void DrawPartContourHalf(DrawingContext ctx, double ox, double oy, double forgingHeight, double outerDiaPrime, double? innerDiaPrime, double heightPrime) { var Rp = outerDiaPrime / 2.0; var h = heightPrime; // 零件轮廓居中于锻件轮廓内 (垂直方向) var offsetY = (forgingHeight - h) / 2.0; var y0 = oy + offsetY; // 计算X方向的范围:从零件内半径到零件外半径 // 如果没有内径,则默认从对称轴(ox)开始 double minX = ox; if (innerDiaPrime.HasValue && innerDiaPrime.Value > 0) { minX = ox + innerDiaPrime.Value / 2.0; } double maxX = ox + Rp; // 绘制闭合方框(对应截图中的黄色虚线框,即零件截面) var polyPart = new Polyline(); polyPart.AddVertexAt(0, new Point2d(minX, y0), 0, 0, 0); polyPart.AddVertexAt(1, new Point2d(maxX, y0), 0, 0, 0); polyPart.AddVertexAt(2, new Point2d(maxX, y0 + h), 0, 0, 0); polyPart.AddVertexAt(3, new Point2d(minX, y0 + h), 0, 0, 0); polyPart.Closed = true; // 闭合方框 ctx.Style?.Apply(polyPart, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(polyPart); ctx.Tr.AddNewlyCreatedDBObject(polyPart, true); } #endregion private static void DrawInnerHole(DrawingContext ctx, double xInnerLeft, double xInnerRight, double yBottom, double height) { var yTop = yBottom + height; var lineLeft = new Line(new Point3d(xInnerLeft, yBottom, 0), new Point3d(xInnerLeft, yTop, 0)); ctx.Style?.Apply(lineLeft, DrawingStyleManager.Role.Hidden); ctx.Btr.AppendEntity(lineLeft); ctx.Tr.AddNewlyCreatedDBObject(lineLeft, true); var lineRight = new Line(new Point3d(xInnerRight, yBottom, 0), new Point3d(xInnerRight, yTop, 0)); ctx.Style?.Apply(lineRight, DrawingStyleManager.Role.Hidden); ctx.Btr.AppendEntity(lineRight); ctx.Tr.AddNewlyCreatedDBObject(lineRight, true); } private static void DrawInnerFillets(DrawingContext ctx, double xInnerLeft, double xInnerRight, double yBottom, double height, double filletR) { // 在内径四角绘制圆角示意 var yTop = yBottom + height; // 左上角 DrawFilletArc(ctx, xInnerLeft + filletR, yTop - filletR, filletR, Math.PI / 2, Math.PI); // 左下角 DrawFilletArc(ctx, xInnerLeft + filletR, yBottom + filletR, filletR, Math.PI, Math.PI * 1.5); // 右上角 DrawFilletArc(ctx, xInnerRight - filletR, yTop - filletR, filletR, 0, Math.PI / 2); // 右下角 DrawFilletArc(ctx, xInnerRight - filletR, yBottom + filletR, filletR, Math.PI * 1.5, Math.PI * 2); } private static void DrawFilletArc(DrawingContext ctx, double cx, double cy, double r, double startAngle, double endAngle) { try { var arc = new Arc(new Point3d(cx, cy, 0), r, startAngle, endAngle); ctx.Style?.Apply(arc, DrawingStyleManager.Role.OutlineThin); ctx.Btr.AppendEntity(arc); ctx.Tr.AddNewlyCreatedDBObject(arc, true); } catch { // 忽略圆弧创建错误 } } private static void DrawOuterDiameterDimension(DrawingContext ctx, double ox, double oy, double width, double diameter, double? tolPlus, double? tolMinus, double? diameterPrime) { // Use OriginalBag for text display to avoid scaled values var diameterVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1) ?? diameter; var diameterStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyOuterDiameter1TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyOuterDiameter1TolMinus); var diameterPrimeVal = diameterPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyOuterDiameter1Prime); if (p.HasValue) diameterPrimeVal = p.Value; } var baseText = BuildDimensionText($"%%c{FormatDimNumber(diameterVal, diameterStr)}", tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = diameterPrimeVal.HasValue && diameterPrimeVal.Value > 0 ? baseText + $"\\X(%%c{FormatDimNumber(diameterPrimeVal.Value)})" : baseText; AddLinearDim( ctx, new Point3d(ox, oy, 0), new Point3d(ox + width, oy, 0), new Point3d(ox + width / 2, oy - 40, 0), 0, dimText); } private static void DrawInnerDiameterDimension(DrawingContext ctx, double xInnerLeft, double xInnerRight, double yBottom, double diameter, double? tolPlus, double? tolMinus, double? diameterPrime) { // Use OriginalBag for text display var diameterVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2) ?? diameter; var diameterStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyInnerDiameter2TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyInnerDiameter2TolMinus); var diameterPrimeVal = diameterPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyInnerDiameter2Prime); if (p.HasValue) diameterPrimeVal = p.Value; } var baseText = BuildDimensionText($"%%c{FormatDimNumber(diameterVal, diameterStr)}", tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = diameterPrimeVal.HasValue && diameterPrimeVal.Value > 0 ? baseText + $"\\X(%%c{FormatDimNumber(diameterPrimeVal.Value)})" : baseText; AddLinearDim( ctx, new Point3d(xInnerLeft, yBottom, 0), new Point3d(xInnerRight, yBottom, 0), new Point3d((xInnerLeft + xInnerRight) / 2, yBottom - 25, 0), 0, dimText); } private static void DrawHeightDimension(DrawingContext ctx, double ox, double oy, double width, double height, double? tolPlus, double? tolMinus, double? heightPrime) { // Use OriginalBag for text display var heightVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1) ?? height; var heightStr = ctx.OriginalBag?.GetString(KeyHeight1); var tolPlusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1TolPlus) ?? tolPlus; var tolMinusVal = ctx.OriginalBag?.GetDoubleOrNull(KeyHeight1TolMinus) ?? tolMinus; var tolPlusStr = ctx.OriginalBag?.GetString(KeyHeight1TolPlus); var tolMinusStr = ctx.OriginalBag?.GetString(KeyHeight1TolMinus); var heightPrimeVal = heightPrime; if (ctx.OriginalBag != null) { var p = ctx.OriginalBag.GetDoubleOrNull(KeyHeight1Prime); if (p.HasValue) heightPrimeVal = p.Value; } var baseText = BuildDimensionText(FormatDimNumber(heightVal, heightStr), tolPlusVal, tolMinusVal, tolPlusStr, tolMinusStr); var dimText = heightPrimeVal.HasValue && heightPrimeVal.Value > 0 ? baseText + $"\\X({FormatDimNumber(heightPrimeVal.Value)})" : baseText; AddLinearDim( ctx, new Point3d(ox + width, oy, 0), new Point3d(ox + width, oy + height, 0), new Point3d(ox + width + 20, oy + height / 2, 0), 90, dimText); } private static void DrawUnspecifiedFilletNote(DrawingContext ctx, double ox, double oy, double R, double height, double filletR) { // 在图纸右上角添加文字说明 try { // Try to get original value to avoid scaling var val = filletR; if (ctx.OriginalBag != null) { // Check likely keys (could be Ring, Disk, Shaft, Box) // We don't strictly know which one triggered this call without passing key, // but we can check the context or just check all if they are mutually exclusive or consistent. // Or better, just check the one that corresponds to current feature type. double? original = null; if (FeatureDrivenDrawer.IsRing(ctx.StructuralFeature)) original = ctx.OriginalBag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax); else if (FeatureDrivenDrawer.IsDisk(ctx.StructuralFeature)) original = ctx.OriginalBag.GetDoubleOrNull(KeyDiskFilletRadiusMax); else if (FeatureDrivenDrawer.IsShaft(ctx.StructuralFeature)) original = ctx.OriginalBag.GetDoubleOrNull(KeyShaftFilletRadiusMax); else if (FeatureDrivenDrawer.IsBlock(ctx.StructuralFeature)) original = ctx.OriginalBag.GetDoubleOrNull(KeyBoxFilletRadiusMax); if (original.HasValue) val = original.Value; } var text = new DBText(); text.TextString = $"未注圆角半径R≤{val}"; text.Position = new Point3d(ox + height + 10, oy + R + 15, 0); text.Height = 5; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { // 忽略 } } private static void DrawMinWallThicknessNote(DrawingContext ctx, double xInnerRight, double xOuterRight, double yBottom, double thickness) { var val = ctx.OriginalBag?.GetDoubleOrNull(KeyMinWallThickness) ?? thickness; var dimText = $"{FormatDimNumber(val)}min"; AddLinearDim( ctx, new Point3d(xInnerRight, yBottom, 0), new Point3d(xOuterRight, yBottom, 0), new Point3d((xInnerRight + xOuterRight) / 2, yBottom - 10, 0), 0, dimText); } private static void DrawPartContour(DrawingContext ctx, Point3d center, double outerDiaPrime, double? innerDiaPrime, double heightPrime) { var w = outerDiaPrime; var h = heightPrime; var x0 = center.X - w / 2.0; var y0 = center.Y - h / 2.0; var polyPart = new Polyline(); polyPart.AddVertexAt(0, new Point2d(x0, y0), 0, 0, 0); polyPart.AddVertexAt(1, new Point2d(x0 + w, y0), 0, 0, 0); polyPart.AddVertexAt(2, new Point2d(x0 + w, y0 + h), 0, 0, 0); polyPart.AddVertexAt(3, new Point2d(x0, y0 + h), 0, 0, 0); polyPart.Closed = true; ctx.Style?.Apply(polyPart, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(polyPart); ctx.Tr.AddNewlyCreatedDBObject(polyPart, true); // 内径(车加工态)轮廓 if (innerDiaPrime.HasValue && innerDiaPrime.Value > 0 && innerDiaPrime.Value < w) { var xiL = center.X - innerDiaPrime.Value / 2.0; var xiR = center.X + innerDiaPrime.Value / 2.0; var yTop = y0 + h; var l1 = new Line(new Point3d(xiL, y0, 0), new Point3d(xiL, yTop, 0)); ctx.Style?.Apply(l1, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(l1); ctx.Tr.AddNewlyCreatedDBObject(l1, true); var l2 = new Line(new Point3d(xiR, y0, 0), new Point3d(xiR, yTop, 0)); ctx.Style?.Apply(l2, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(l2); ctx.Tr.AddNewlyCreatedDBObject(l2, true); } } private static void DrawRingSectionHatch(DrawingContext ctx, double xInnerRight, double xOuterRight, double yBottom, double height) { if (xOuterRight <= xInnerRight) { return; } try { // 临时边界(用于剖面填充),填充后删除边界 var boundary = new Polyline(); boundary.AddVertexAt(0, new Point2d(xInnerRight, yBottom), 0, 0, 0); boundary.AddVertexAt(1, new Point2d(xOuterRight, yBottom), 0, 0, 0); boundary.AddVertexAt(2, new Point2d(xOuterRight, yBottom + height), 0, 0, 0); boundary.AddVertexAt(3, new Point2d(xInnerRight, 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 { // ignore } } catch { // ignore } } 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; } // Format: 0.00... based on count // If decimals is 0, format is "0" (integer) 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 } // Get number of digits after decimal int autoDecimals = str.Length - decimalIndex - 1; // Construct format string if (autoDecimals == 0) return str; string format = "0." + new string('0', autoDecimals); return value.ToString(format); } catch { return value.ToString(); } } #endregion #region 饼盘绘制 - 特征驱动 private static void DrawDiskFeatures(DrawingContext ctx) { var bag = ctx.Bag; var diameter = bag.GetDoubleOrNull(KeyDiskDiameter); var height = bag.GetDoubleOrNull(KeyDiskHeight); if (!diameter.HasValue || diameter.Value <= 0) return; if (!height.HasValue || height.Value <= 0) return; double ox = ctx.Center.X - diameter.Value / 2.0; double oy = ctx.Center.Y - height.Value / 2.0; double R = diameter.Value / 2.0; double H = height.Value; // 绘制饼盘轮廓(俯视图为圆,侧视图为矩形) // 这里绘制侧视图 var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0); poly.AddVertexAt(1, new Point2d(ox + diameter.Value, oy), 0, 0, 0); poly.AddVertexAt(2, new Point2d(ox + diameter.Value, oy + H), 0, 0, 0); poly.AddVertexAt(3, new Point2d(ox, oy + H), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); // 直径标注 var diaTolPlus = bag.GetDoubleOrNull(KeyDiskDiameterTolPlus); var diaTolMinus = bag.GetDoubleOrNull(KeyDiskDiameterTolMinus); var diameterStr = bag.GetString(KeyDiskDiameter); var diaTolPlusStr = bag.GetString(KeyDiskDiameterTolPlus); var diaTolMinusStr = bag.GetString(KeyDiskDiameterTolMinus); var diaDimText = BuildDimensionText($"%%c{FormatDimNumber(diameter.Value, diameterStr)}", diaTolPlus, diaTolMinus, diaTolPlusStr, diaTolMinusStr); AddLinearDim(ctx, new Point3d(ox, oy, 0), new Point3d(ox + diameter.Value, oy, 0), new Point3d(ctx.Center.X, oy - 20, 0), 0, diaDimText); // 高度标注 var htTolPlus = bag.GetDoubleOrNull(KeyDiskHeightTolPlus); var htTolMinus = bag.GetDoubleOrNull(KeyDiskHeightTolMinus); var heightStr = bag.GetString(KeyDiskHeight); var htTolPlusStr = bag.GetString(KeyDiskHeightTolPlus); var htTolMinusStr = bag.GetString(KeyDiskHeightTolMinus); var htDimText = BuildDimensionText(FormatDimNumber(H, heightStr), htTolPlus, htTolMinus, htTolPlusStr, htTolMinusStr); AddLinearDim(ctx, new Point3d(ox + diameter.Value, oy, 0), new Point3d(ox + diameter.Value, oy + H, 0), new Point3d(ox + diameter.Value + 20, ctx.Center.Y, 0), 90, htDimText); // 未注圆角(使用饼盘专用Key) var unspecifiedFillet = bag.GetDoubleOrNull(KeyDiskFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { DrawUnspecifiedFilletNote(ctx, ox, oy + H, R, diameter.Value, unspecifiedFillet.Value); } // 零件尺寸(如果有) var diaPrime = bag.GetDoubleOrNull(KeyDiskDiameterPrime); var heightPrime = bag.GetDoubleOrNull(KeyDiskHeightPrime); if (diaPrime.HasValue && diaPrime.Value > 0 && heightPrime.HasValue && heightPrime.Value > 0) { DrawDiskPartContour(ctx, ox, oy, diameter.Value, H, diaPrime.Value, heightPrime.Value); } } private static void DrawDiskPartContour(DrawingContext ctx, double ox, double oy, double forgingDia, double forgingH, double partDia, double partH) { double offsetX = (forgingDia - partDia) / 2.0; double offsetY = (forgingH - partH) / 2.0; var polyPart = new Polyline(); polyPart.AddVertexAt(0, new Point2d(ox + offsetX, oy + offsetY), 0, 0, 0); polyPart.AddVertexAt(1, new Point2d(ox + offsetX + partDia, oy + offsetY), 0, 0, 0); polyPart.AddVertexAt(2, new Point2d(ox + offsetX + partDia, oy + offsetY + partH), 0, 0, 0); polyPart.AddVertexAt(3, new Point2d(ox + offsetX, oy + offsetY + partH), 0, 0, 0); polyPart.Closed = true; ctx.Style?.Apply(polyPart, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(polyPart); ctx.Tr.AddNewlyCreatedDBObject(polyPart, true); // 不再对零件轮廓整块填充剖面线 // 零件直径标注 AddLinearDim(ctx, new Point3d(ox + offsetX, oy + offsetY, 0), new Point3d(ox + offsetX + partDia, oy + offsetY, 0), new Point3d(ox + offsetX + partDia / 2, oy + offsetY - 10, 0), 0, $"%%c{partDia}"); // 零件高度标注 AddLinearDim(ctx, new Point3d(ox + offsetX + partDia, oy + offsetY, 0), new Point3d(ox + offsetX + partDia, oy + offsetY + partH, 0), new Point3d(ox + offsetX + partDia + 10, oy + offsetY + partH / 2, 0), 90, $"{partH}"); } #endregion #region 轴杆绘制 - 特征驱动 private static void DrawShaftFeatures(DrawingContext ctx) { var bag = ctx.Bag; // 圆轴使用Diameter/Length,方轴使用BoxSize1/2/3 var isSquareShaft = !string.IsNullOrWhiteSpace(ctx.SpecialCondition) && ctx.SpecialCondition.IndexOf("方轴", StringComparison.OrdinalIgnoreCase) >= 0; if (isSquareShaft) { // 方轴使用方体参数绘制 DrawSquareShaftFeatures(ctx); return; } // 圆轴绘制 var diameter = bag.GetDoubleOrNull(KeyDiameter); var length = bag.GetDoubleOrNull(KeyLength); if (!diameter.HasValue || diameter.Value <= 0) return; if (!length.HasValue || length.Value <= 0) return; double ox = ctx.Center.X - length.Value / 2.0; double oy = ctx.Center.Y; double R = diameter.Value / 2.0; double L = length.Value; TryLoadLinetype(ctx.Db, ctx.Tr, "CENTER"); // 绘制轴杆轮廓(侧视图为矩形) var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy - R), 0, 0, 0); poly.AddVertexAt(1, new Point2d(ox + L, oy - R), 0, 0, 0); poly.AddVertexAt(2, new Point2d(ox + L, oy + R), 0, 0, 0); poly.AddVertexAt(3, new Point2d(ox, oy + R), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); // 中心线 var centerLine = new Line(new Point3d(ox - 10, oy, 0), new Point3d(ox + L + 10, oy, 0)); ctx.Style?.Apply(centerLine, DrawingStyleManager.Role.Centerline); ctx.Btr.AppendEntity(centerLine); ctx.Tr.AddNewlyCreatedDBObject(centerLine, true); // 直径标注 var diaTolPlus = bag.GetDoubleOrNull(KeyDiameterTolPlus); var diaTolMinus = bag.GetDoubleOrNull(KeyDiameterTolMinus); var diameterStr = bag.GetString(KeyDiameter); var diaTolPlusStr = bag.GetString(KeyDiameterTolPlus); var diaTolMinusStr = bag.GetString(KeyDiameterTolMinus); var diaDimText = BuildDimensionText($"%%c{FormatDimNumber(diameter.Value, diameterStr)}", diaTolPlus, diaTolMinus, diaTolPlusStr, diaTolMinusStr); AddLinearDim(ctx, new Point3d(ox, oy + R, 0), new Point3d(ox, oy - R, 0), new Point3d(ox - 20, oy, 0), 90, diaDimText); // 长度标注 var lenTolPlus = bag.GetDoubleOrNull(KeyLengthTolPlus); var lenTolMinus = bag.GetDoubleOrNull(KeyLengthTolMinus); var lengthStr = bag.GetString(KeyLength); var lenTolPlusStr = bag.GetString(KeyLengthTolPlus); var lenTolMinusStr = bag.GetString(KeyLengthTolMinus); var lenDimText = BuildDimensionText(FormatDimNumber(L, lengthStr), lenTolPlus, lenTolMinus, lenTolPlusStr, lenTolMinusStr); AddLinearDim(ctx, new Point3d(ox, oy - R, 0), new Point3d(ox + L, oy - R, 0), new Point3d(ctx.Center.X, oy - R - 20, 0), 0, lenDimText); // 未注圆角(使用轴杆专用Key) var unspecifiedFillet = bag.GetDoubleOrNull(KeyShaftFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { DrawUnspecifiedFilletNote(ctx, ox, oy, R, L, unspecifiedFillet.Value); } // 零件尺寸(如果有) var diaPrime = bag.GetDoubleOrNull(KeyDiameterPrime); var lenPrime = bag.GetDoubleOrNull(KeyLengthPrime); if (diaPrime.HasValue && diaPrime.Value > 0 && lenPrime.HasValue && lenPrime.Value > 0) { DrawShaftPartContour(ctx, ox, oy, R, L, diaPrime.Value, lenPrime.Value); } } /// /// 方轴绘制(使用方体参数) /// private static void DrawSquareShaftFeatures(DrawingContext ctx) { var bag = ctx.Bag; var size1 = bag.GetDoubleOrNull(KeyBoxSize1); var size2 = bag.GetDoubleOrNull(KeyBoxSize2); var size3 = bag.GetDoubleOrNull(KeyBoxSize3); if (!size1.HasValue || size1.Value <= 0) return; double width = size1.Value; double height = size2.HasValue && size2.Value > 0 ? size2.Value : size1.Value; double depth = size3.HasValue && size3.Value > 0 ? size3.Value : 0; double ox = ctx.Center.X - width / 2.0; double oy = ctx.Center.Y - height / 2.0; // 绘制正视图(矩形) var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0); poly.AddVertexAt(1, new Point2d(ox + width, oy), 0, 0, 0); poly.AddVertexAt(2, new Point2d(ox + width, oy + height), 0, 0, 0); poly.AddVertexAt(3, new Point2d(ox, oy + height), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); // 尺寸1标注 var tol1Plus = bag.GetDoubleOrNull(KeyBoxSize1TolPlus); var tol1Minus = bag.GetDoubleOrNull(KeyBoxSize1TolMinus); var dim1Text = BuildDimensionText($"{width}", tol1Plus, tol1Minus); AddLinearDim(ctx, new Point3d(ox, oy, 0), new Point3d(ox + width, oy, 0), new Point3d(ctx.Center.X, oy - 20, 0), 0, dim1Text); // 尺寸2标注 if (size2.HasValue && size2.Value > 0) { var tol2Plus = bag.GetDoubleOrNull(KeyBoxSize2TolPlus); var tol2Minus = bag.GetDoubleOrNull(KeyBoxSize2TolMinus); var dim2Text = BuildDimensionText($"{height}", tol2Plus, tol2Minus); AddLinearDim(ctx, new Point3d(ox + width, oy, 0), new Point3d(ox + width, oy + height, 0), new Point3d(ox + width + 20, ctx.Center.Y, 0), 90, dim2Text); } // 尺寸3标注 if (depth > 0) { var tol3Plus = bag.GetDoubleOrNull(KeyBoxSize3TolPlus); var tol3Minus = bag.GetDoubleOrNull(KeyBoxSize3TolMinus); var dim3Text = BuildDimensionText($"深度:{depth}", tol3Plus, tol3Minus); try { var text = new DBText(); text.TextString = dim3Text; text.Position = new Point3d(ox + width + 10, oy + height + 10, 0); text.Height = 5; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { } } // 未注圆角(使用方体专用Key) var unspecifiedFillet = bag.GetDoubleOrNull(KeyBoxFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { try { var text = new DBText(); text.TextString = $"未注圆角半径R≤{unspecifiedFillet.Value}"; text.Position = new Point3d(ox + width + 10, oy + height + 20, 0); text.Height = 5; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { } } // 零件尺寸 var size1Prime = bag.GetDoubleOrNull(KeyBoxSize1Prime); var size2Prime = bag.GetDoubleOrNull(KeyBoxSize2Prime); var size3Prime = bag.GetDoubleOrNull(KeyBoxSize3Prime); if (size1Prime.HasValue && size1Prime.Value > 0) { DrawBlockPartContour(ctx, ox, oy, width, height, size1Prime.Value, size2Prime ?? size1Prime.Value, size3Prime); } } private static void DrawShaftPartContour(DrawingContext ctx, double ox, double oy, double R, double L, double partDia, double partLen) { double offsetX = (L - partLen) / 2.0; double Rp = partDia / 2.0; var polyPart = new Polyline(); polyPart.AddVertexAt(0, new Point2d(ox + offsetX, oy - Rp), 0, 0, 0); polyPart.AddVertexAt(1, new Point2d(ox + offsetX + partLen, oy - Rp), 0, 0, 0); polyPart.AddVertexAt(2, new Point2d(ox + offsetX + partLen, oy + Rp), 0, 0, 0); polyPart.AddVertexAt(3, new Point2d(ox + offsetX, oy + Rp), 0, 0, 0); polyPart.Closed = true; ctx.Style?.Apply(polyPart, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(polyPart); ctx.Tr.AddNewlyCreatedDBObject(polyPart, true); // 不再对零件轮廓整块填充剖面线 // 零件直径标注 AddLinearDim(ctx, new Point3d(ox + offsetX, oy + Rp, 0), new Point3d(ox + offsetX, oy - Rp, 0), new Point3d(ox + offsetX - 10, oy, 0), 90, $"%%c{partDia}"); // 零件长度标注 AddLinearDim(ctx, new Point3d(ox + offsetX, oy - Rp, 0), new Point3d(ox + offsetX + partLen, oy - Rp, 0), new Point3d(ox + offsetX + partLen / 2, oy - Rp - 10, 0), 0, $"{partLen}"); } #endregion #region 方形绘制 - 特征驱动 private static void DrawBlockFeatures(DrawingContext ctx) { var bag = ctx.Bag; // 使用方体专用参数 var size1 = bag.GetDoubleOrNull(KeyBoxSize1); var size2 = bag.GetDoubleOrNull(KeyBoxSize2); var size3 = bag.GetDoubleOrNull(KeyBoxSize3); if (!size1.HasValue || size1.Value <= 0) return; // 默认为正方形或使用size1 double width = size1.Value; double height = size2.HasValue && size2.Value > 0 ? size2.Value : size1.Value; double depth = size3.HasValue && size3.Value > 0 ? size3.Value : 0; double ox = ctx.Center.X - width / 2.0; double oy = ctx.Center.Y - height / 2.0; // 绘制正视图(矩形) var poly = new Polyline(); poly.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0); poly.AddVertexAt(1, new Point2d(ox + width, oy), 0, 0, 0); poly.AddVertexAt(2, new Point2d(ox + width, oy + height), 0, 0, 0); poly.AddVertexAt(3, new Point2d(ox, oy + height), 0, 0, 0); poly.Closed = true; ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(poly); ctx.Tr.AddNewlyCreatedDBObject(poly, true); // 宽度标注 (BoxSize1) var tol1Plus = bag.GetDoubleOrNull(KeyBoxSize1TolPlus); var tol1Minus = bag.GetDoubleOrNull(KeyBoxSize1TolMinus); var widthStr = bag.GetString(KeyBoxSize1); var tol1PlusStr = bag.GetString(KeyBoxSize1TolPlus); var tol1MinusStr = bag.GetString(KeyBoxSize1TolMinus); var dim1Text = BuildDimensionText(FormatDimNumber(width, widthStr), tol1Plus, tol1Minus, tol1PlusStr, tol1MinusStr); AddLinearDim(ctx, new Point3d(ox, oy, 0), new Point3d(ox + width, oy, 0), new Point3d(ctx.Center.X, oy - 20, 0), 0, dim1Text); // 高度标注 (BoxSize2) if (size2.HasValue && size2.Value > 0) { var tol2Plus = bag.GetDoubleOrNull(KeyBoxSize2TolPlus); var tol2Minus = bag.GetDoubleOrNull(KeyBoxSize2TolMinus); var heightStr = bag.GetString(KeyBoxSize2); var tol2PlusStr = bag.GetString(KeyBoxSize2TolPlus); var tol2MinusStr = bag.GetString(KeyBoxSize2TolMinus); var dim2Text = BuildDimensionText(FormatDimNumber(height, heightStr), tol2Plus, tol2Minus, tol2PlusStr, tol2MinusStr); AddLinearDim(ctx, new Point3d(ox + width, oy, 0), new Point3d(ox + width, oy + height, 0), new Point3d(ox + width + 20, ctx.Center.Y, 0), 90, dim2Text); } // 深度标注 (BoxSize3) - 如果有的话,以文字形式标注 if (depth > 0) { var tol3Plus = bag.GetDoubleOrNull(KeyBoxSize3TolPlus); var tol3Minus = bag.GetDoubleOrNull(KeyBoxSize3TolMinus); var depthStr = bag.GetString(KeyBoxSize3); var tol3PlusStr = bag.GetString(KeyBoxSize3TolPlus); var tol3MinusStr = bag.GetString(KeyBoxSize3TolMinus); var dim3Text = BuildDimensionText($"深度:{FormatDimNumber(depth, depthStr)}", tol3Plus, tol3Minus, tol3PlusStr, tol3MinusStr); try { var text = new DBText(); text.TextString = dim3Text; text.Position = new Point3d(ox + width + 10, oy + height + 10, 0); text.Height = 5; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { } } // 未注圆角(使用方体专用Key) var unspecifiedFillet = bag.GetDoubleOrNull(KeyBoxFilletRadiusMax); if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0) { try { var text = new DBText(); text.TextString = $"未注圆角半径R≤{unspecifiedFillet.Value}"; text.Position = new Point3d(ox + width + 10, oy + height + 20, 0); text.Height = 5; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { } } // 有圆头时,绘制两端半圆形圆头 if (ctx.HasRoundHead) { double R = height / 2.0; // 圆头半径 = 高度/2(示意性) double centerY = oy + height / 2.0; // 左端圆头(半圆) try { var arcLeft = new Arc(new Point3d(ox, centerY, 0), R, Math.PI / 2, Math.PI * 1.5); ctx.Style?.Apply(arcLeft, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(arcLeft); ctx.Tr.AddNewlyCreatedDBObject(arcLeft, true); } catch { } // 右端圆头(半圆) try { var arcRight = new Arc(new Point3d(ox + width, centerY, 0), R, -Math.PI / 2, Math.PI / 2); ctx.Style?.Apply(arcRight, DrawingStyleManager.Role.OutlineBold); ctx.Btr.AppendEntity(arcRight); ctx.Tr.AddNewlyCreatedDBObject(arcRight, true); } catch { } } // 零件尺寸 var size1Prime = bag.GetDoubleOrNull(KeyBoxSize1Prime); var size2Prime = bag.GetDoubleOrNull(KeyBoxSize2Prime); var size3Prime = bag.GetDoubleOrNull(KeyBoxSize3Prime); if (size1Prime.HasValue && size1Prime.Value > 0) { DrawBlockPartContour(ctx, ox, oy, width, height, size1Prime.Value, size2Prime ?? size1Prime.Value, size3Prime); } } private static void DrawBlockPartContour(DrawingContext ctx, double ox, double oy, double forgingW, double forgingH, double partW, double partH, double? partD) { double offsetX = (forgingW - partW) / 2.0; double offsetY = (forgingH - partH) / 2.0; var polyPart = new Polyline(); polyPart.AddVertexAt(0, new Point2d(ox + offsetX, oy + offsetY), 0, 0, 0); polyPart.AddVertexAt(1, new Point2d(ox + offsetX + partW, oy + offsetY), 0, 0, 0); polyPart.AddVertexAt(2, new Point2d(ox + offsetX + partW, oy + offsetY + partH), 0, 0, 0); polyPart.AddVertexAt(3, new Point2d(ox + offsetX, oy + offsetY + partH), 0, 0, 0); polyPart.Closed = true; ctx.Style?.Apply(polyPart, DrawingStyleManager.Role.PartContour); ctx.Btr.AppendEntity(polyPart); ctx.Tr.AddNewlyCreatedDBObject(polyPart, true); // 不再对零件轮廓整块填充剖面线 // 零件尺寸1标注 AddLinearDim(ctx, new Point3d(ox + offsetX, oy + offsetY, 0), new Point3d(ox + offsetX + partW, oy + offsetY, 0), new Point3d(ox + offsetX + partW / 2, oy + offsetY - 10, 0), 0, $"{partW}"); // 零件尺寸2标注 AddLinearDim(ctx, new Point3d(ox + offsetX + partW, oy + offsetY, 0), new Point3d(ox + offsetX + partW, oy + offsetY + partH, 0), new Point3d(ox + offsetX + partW + 10, oy + offsetY + partH / 2, 0), 90, $"{partH}"); // 零件尺寸3标注(如果有) if (partD.HasValue && partD.Value > 0) { try { var text = new DBText(); text.TextString = $"深度:{partD.Value}"; text.Position = new Point3d(ox + offsetX + partW + 10, oy + offsetY + partH + 5, 0); text.Height = 4; ctx.Style?.Apply(text, DrawingStyleManager.Role.Text); ctx.Btr.AppendEntity(text); ctx.Tr.AddNewlyCreatedDBObject(text, true); } catch { } } } #endregion #region 辅助方法 private static string BuildDimensionText(string baseText, double? tolPlus, double? tolMinus, string tolPlusStr = null, string tolMinusStr = null) { if (!tolPlus.HasValue && !tolMinus.HasValue) { return baseText; } // Check if symmetrical // tolMinus is typically positive in parameter bag representing absolute deviation (e.g. 0.02 for -0.02) // or negative. We check absolute values. bool isSymmetrical = false; if (tolPlus.HasValue && tolMinus.HasValue) { if (Math.Abs(Math.Abs(tolPlus.Value) - Math.Abs(tolMinus.Value)) < 0.000001) { // Also check if raw strings (if provided) imply different precision? // E.g. +0.50 vs -0.5. Strictly speaking they are same value but different text. // But for symmetrical display %%p0.50, we pick one precision (usually plus). isSymmetrical = true; } } // User requested Height scale 0.7 (70% of base text height) // Symmetric tolerance: ±Value if (isSymmetrical) { var val = Math.Abs(tolPlus.Value); // Use tolPlusStr for precision if available return $"{baseText}%%p{FormatDimNumber(val, tolPlusStr)}"; } // Deviation tolerance: Stacked // AutoCAD MText stack: \H...x;\S...^...; (Caret ^ for tolerance style stacking without bar) // User requested Height scale 0.7 (70% of base text height) 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) { // Lower deviation is usually displayed as negative number. double vm = tolMinus.Value; // Use FormatDimNumber with raw string to respect input precision string formattedVal = FormatDimNumber(Math.Abs(vm), tolMinusStr); if (Math.Abs(vm) < 0.000001) { minusStr = formattedVal; } else { // Always display lower tolerance with negative sign minusStr = $"-{formattedVal}"; } } // If we have both, stack them if (tolPlus.HasValue && tolMinus.HasValue) { // Add a space between base text and tolerance stack to prevent crowding return $"{baseText} {{\\H{tolScale}x;\\S{plusStr}^{minusStr};}}"; } // If only one side is present var single = tolPlus.HasValue ? plusStr : minusStr; return $"{baseText} {{\\H{tolScale}x;{single}}}"; } private static void ApplyHalfSideDimStyle(Dimension dim, DrawingContext ctx) { // 按需求修改半边标注样式: // 尺寸线 1和2开 (默认即为开) // 界线1关 (Dimse1 = true) - 对称轴处无尺寸界线 // 界线2开 (Dimse2 = false) TrySetDimProp(dim, "Dimse1", true); TrySetDimProp(dim, "Dimse2", false); // 箭头1 无 (Dimblk1 = _NONE) // 箭头2 实心闭合 (Dimblk2 = Default) try { // 需要设置 Dimsah = true 才能分别设置箭头 TrySetDimProp(dim, "Dimsah", true); // [Modified] Ensure _NONE block exists, then use it. // Restore line display (remove Dimsd1=true) so it aligns with left side logic. EnsureNoneBlock(ctx.Db, ctx.Tr); var bt = (BlockTable)ctx.Tr.GetObject(ctx.Db.BlockTableId, OpenMode.ForRead); // Now we are confident _NONE exists or defaults to something safe if (bt.Has("_NONE")) { dim.Dimblk1 = bt["_NONE"]; } } catch { } } private static void EnsureNoneBlock(Database db, Transaction tr) { try { var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); if (!bt.Has("_NONE")) { // Create empty block named _NONE using (var btr = new BlockTableRecord()) { btr.Name = "_NONE"; btr.Origin = Point3d.Origin; bt.UpgradeOpen(); bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); } } } catch { } } private static void AddLinearDim(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; // Use current drawing's dimension style (template-controlled). var dim = new RotatedDimension(rotRad, pt1, pt2, dimLinePt, textOverride, ctx.Db.Dimstyle); try { dim.SetDatabaseDefaults(); } catch { } // [Modified] Explicitly set ColorIndex to 3 (Green) as requested by user dim.ColorIndex = 3; // Re-apply text override because SetDatabaseDefaults might have cleared it if (!string.IsNullOrEmpty(textOverride)) { dim.DimensionText = textOverride; } try { dim.Normal = Vector3d.ZAxis; } catch { } TryApplyDimSizeOverrides(dim); TryApplyDimLayoutOverrides(dim, pt1, pt2, dimLinePt, rotRad); customizer?.Invoke(dim, ctx); ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension); // Re-force color after style application just in case dim.ColorIndex = 3; ctx.Btr.AppendEntity(dim); ctx.Tr.AddNewlyCreatedDBObject(dim, true); // Ensure dimension graphics (including text) are computed and persisted in the DWG. try { dim.RecomputeDimensionBlock(true); } catch { // ignore } } finally { HostApplicationServices.WorkingDatabase = prevDb; } } catch { // 忽略标注错误 } } private static void TryApplyDimSizeOverrides(Dimension dim) { if (dim == null) { return; } // Template dimstyle may have very small DIMTXT; apply per-dimension overrides to keep it readable. // Values are in drawing units (typically mm). 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); // Updated based on user request TrySetDimProp(dim, "Dimtofl", true); // Draw dim line between ext lines TrySetDimProp(dim, "Dimatfit", 3); // Text or Arrows (Best fit) TrySetDimProp(dim, "Dimtad", 1); // Text Vertical: Above // [Modified] Change dimension color to Green (3) as requested TrySetDimProp(dim, "Dimclrd", 3); // Dimension Line Color TrySetDimProp(dim, "Dimclre", 3); // Extension Line Color TrySetDimProp(dim, "Dimclrt", 3); // Text Color } private static void TryApplyDimLayoutOverrides(Dimension dim, Point3d pt1, Point3d pt2, Point3d dimLinePt, double rotRad) { if (dim == null) { return; } // 1) 文本与尺寸线平行(避免模板把文字强制水平)。 TrySetDimProp(dim, "Dimtih", false); TrySetDimProp(dim, "Dimtoh", false); // 2) 不抑制尺寸界线(修复半圆图左侧中心线位置“尺寸界线为空”的情况)。 TrySetDimProp(dim, "Dimse1", false); TrySetDimProp(dim, "Dimse2", false); // 3) 尺寸线位于尺寸(文字)上方:将文字整体移到尺寸线的“下方”(相对尺寸线法向的顺时针侧)。 // Modified: User requested "Above" style. Dimtad=1 handles this. Manual positioning removed. /* try { var dimtxt = TryGetDoubleProp(dim, "Dimtxt") ?? 3.5; var dimgap = TryGetDoubleProp(dim, "Dimgap") ?? 1.0; var offset = Math.Max(1.0, dimtxt + dimgap); var v = new Vector3d(-Math.Sin(rotRad), Math.Cos(rotRad), 0); // 尺寸线法向(逆时针) var q1 = ProjectToLineAlongNormal(pt1, dimLinePt, v); var q2 = ProjectToLineAlongNormal(pt2, dimLinePt, v); var mid = new Point3d((q1.X + q2.X) / 2.0, (q1.Y + q2.Y) / 2.0, 0); var textPos = mid - v.MultiplyBy(offset); TrySetDimProp(dim, "TextPosition", textPos); TrySetDimProp(dim, "TextRotation", rotRad); } catch { // ignore } */ } private static Point3d ProjectToLineAlongNormal(Point3d p, Point3d linePoint, Vector3d normal) { // 线:通过 linePoint,方向与 normal 垂直(即 (x-linePoint)·normal = 0) // 沿 normal 方向投影:q = p - normal * ((p-linePoint)·normal) var d = (p - linePoint).DotProduct(normal); return p - normal.MultiplyBy(d); } private static double? TryGetDoubleProp(object obj, string propName) { if (obj == null || string.IsNullOrWhiteSpace(propName)) { return null; } try { var prop = obj.GetType().GetProperty(propName); if (prop == null || !prop.CanRead) { return null; } var v = prop.GetValue(obj, null); if (v is double d) return d; if (v is float f) return f; if (v is int i) return i; if (v is short s) return s; return null; } catch { return null; } } private static void TrySetDimProp(object obj, string propName, double value) { if (obj == null || string.IsNullOrWhiteSpace(propName)) { return; } try { var prop = obj.GetType().GetProperty(propName); if (prop == null || !prop.CanWrite) { return; } if (prop.PropertyType == typeof(double)) { prop.SetValue(obj, value, null); return; } if (prop.PropertyType == typeof(float)) { prop.SetValue(obj, (float)value, null); return; } if (prop.PropertyType == typeof(int)) { prop.SetValue(obj, (int)Math.Round(value), null); return; } if (prop.PropertyType == typeof(short)) { prop.SetValue(obj, (short)Math.Round(value), null); return; } if (prop.PropertyType == typeof(bool)) { prop.SetValue(obj, Math.Abs(value) > 0.000001, null); return; } } catch { // ignore } } private static void TrySetDimProp(object obj, string propName, bool value) { if (obj == null || string.IsNullOrWhiteSpace(propName)) { return; } try { var prop = obj.GetType().GetProperty(propName); if (prop == null || !prop.CanWrite) { return; } if (prop.PropertyType == typeof(bool)) { prop.SetValue(obj, value, null); return; } if (prop.PropertyType == typeof(short)) { prop.SetValue(obj, (short)(value ? 1 : 0), null); return; } if (prop.PropertyType == typeof(int)) { prop.SetValue(obj, value ? 1 : 0, null); } } catch { // ignore } } private static void TrySetDimProp(object obj, string propName, short value) { if (obj == null || string.IsNullOrWhiteSpace(propName)) { return; } try { var prop = obj.GetType().GetProperty(propName); if (prop == null || !prop.CanWrite) { return; } if (prop.PropertyType == typeof(short)) { prop.SetValue(obj, value, null); return; } if (prop.PropertyType == typeof(int)) { prop.SetValue(obj, (int)value, null); return; } if (prop.PropertyType == typeof(double)) { prop.SetValue(obj, (double)value, null); } } catch { // ignore } } private static void TrySetDimProp(object obj, string propName, Point3d value) { if (obj == null || string.IsNullOrWhiteSpace(propName)) { return; } try { var prop = obj.GetType().GetProperty(propName); if (prop == null || !prop.CanWrite) { return; } if (prop.PropertyType == typeof(Point3d)) { prop.SetValue(obj, value, null); } } catch { // ignore } } private static void TryLoadLinetype(Database db, Transaction tr, string linetypeName) { try { var linetypeTbl = (LinetypeTable)tr.GetObject(db.LinetypeTableId, OpenMode.ForRead); if (!linetypeTbl.Has(linetypeName)) { try { db.LoadLineTypeFile(linetypeName, "acad.lin"); } catch { } } } catch { } } /// /// 对参数包中的尺寸参数应用缩放比例 /// private static ParamBag ScaleParamBag(ParamBag original, double scaleFactor) { if (original == null || scaleFactor <= 0 || scaleFactor >= 1.0) { return original; } var scaled = new ParamBag(); // 需要缩放的尺寸参数Key列表 var sizeKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { // 环形参数 KeyOuterDiameter1, KeyInnerDiameter2, KeyHeight1, KeyOuterDiameter1Prime, KeyInnerDiameter2Prime, KeyHeight1Prime, KeyMinWallThickness, // 饼盘参数 KeyDiskDiameter, KeyDiskHeight, KeyDiskDiameterPrime, KeyDiskHeightPrime, // 轴杆参数 KeyDiameter, KeyLength, KeyDiameterPrime, KeyLengthPrime, // 方体参数 KeyBoxSize1, KeyBoxSize2, KeyBoxSize3, KeyBoxSize1Prime, KeyBoxSize2Prime, KeyBoxSize3Prime, // 圆角参数 KeyUnspecifiedFilletRadiusMax, KeyInnerRadiusMax, KeyDiskFilletRadiusMax, KeyShaftFilletRadiusMax, KeyBoxFilletRadiusMax, KeyBoxRoundHeadFilletRadiusMax }; // 公差参数也需要缩放 var toleranceKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { KeyOuterDiameter1TolPlus, KeyOuterDiameter1TolMinus, KeyInnerDiameter2TolPlus, KeyInnerDiameter2TolMinus, KeyHeight1TolPlus, KeyHeight1TolMinus, KeyDiskDiameterTolPlus, KeyDiskDiameterTolMinus, KeyDiskHeightTolPlus, KeyDiskHeightTolMinus, KeyDiameterTolPlus, KeyDiameterTolMinus, KeyLengthTolPlus, KeyLengthTolMinus, KeyBoxSize1TolPlus, KeyBoxSize1TolMinus, KeyBoxSize2TolPlus, KeyBoxSize2TolMinus, KeyBoxSize3TolPlus, KeyBoxSize3TolMinus }; foreach (var key in original.GetKeys()) { var value = original.GetString(key); if (sizeKeys.Contains(key) || toleranceKeys.Contains(key)) { // 尝试解析为数值并缩放 if (double.TryParse(value, out var numValue)) { var scaledValue = numValue * scaleFactor; // 保留适当的小数位数 scaled.Set(key, scaledValue.ToString("F2")); } else { scaled.Set(key, value); } } else { // 非尺寸参数直接复制 scaled.Set(key, value); } } return scaled; } #endregion private static void DrawSpecialInnerHoleLeader(DrawingContext ctx, double ox, double oy, double height, double innerDia) { try { // 内孔右侧X坐标 (锻件) double xInner = ox + innerDia / 2.0; // 目标Y坐标:锻件内孔右上角向下偏移一点 (引线不应直指角点,而是指在直线上) double yTarget = oy + height - 5.0; // 箭头点 (指向内孔竖线) var p1 = new Point3d(xInner, yTarget, 0); // 转折点 (向左上) var p2 = new Point3d(xInner - 12, yTarget + 12, 0); // 终点 (水平向左, 长度约35适配文字宽度 "HB5936-13") var p3 = new Point3d(p2.X - 35, p2.Y, 0); // 使用 Leader 实体绘制带箭头的引线 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 = "HB5936-13"; text.Height = 3.5; text.ColorIndex = 7; // 白色 text.Layer = "0"; // 文字位置:左对齐,基点为 p3 (线段最左端) // 位于线段上方 (VerticalMode = TextBottom) 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 { // ignore } } } }