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 KeySquareShaftSize1 = "SquareShaftSize1";
public const string KeySquareShaftSize1TolPlus = "SquareShaftSize1TolPlus";
public const string KeySquareShaftSize1TolMinus = "SquareShaftSize1TolMinus";
public const string KeySquareShaftSize2 = "SquareShaftSize2";
public const string KeySquareShaftSize2TolPlus = "SquareShaftSize2TolPlus";
public const string KeySquareShaftSize2TolMinus = "SquareShaftSize2TolMinus";
public const string KeySquareShaftSize3 = "SquareShaftSize3";
public const string KeySquareShaftSize3TolPlus = "SquareShaftSize3TolPlus";
public const string KeySquareShaftSize3TolMinus = "SquareShaftSize3TolMinus";
public const string KeySquareShaftFilletRadiusMax = "SquareShaftFilletRadiusMax"; // 未注圆角半径R≤
public const string KeySquareShaftSize1Prime = "SquareShaftSize1Prime";
public const string KeySquareShaftSize2Prime = "SquareShaftSize2Prime";
public const string KeySquareShaftSize3Prime = "SquareShaftSize3Prime";
// 方体参数
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
public const string KeyHardness = "Hardness"; // 硬度
public const string KeyMarkingContent = "MarkingContent"; // 标刻内容
#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
&& 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 l = bag.GetDoubleOrNull(KeySquareShaftSize1);
if (l.HasValue)
{
var w = bag.GetDoubleOrNull(KeySquareShaftSize2) ?? 0;
var extraMarginSq = 40;
return new ExpectedDrawingSize
{
Width = l.Value + extraMarginSq * 2,
Height = w + extraMarginSq * 2
};
}
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))
{
if (deliveryStatus == "毛料态" && processMethod == "自由锻" && specialCondition == "圆轴")
{
ShaftRawFreeForgeRoundShaftDrawer.Draw(context.Ctx, context.OriginalBag ?? context.Bag, context.Center, scaleFactor);
}
else if (deliveryStatus == "毛料态" && processMethod == "自由锻" && specialCondition == "方轴")
{
ShaftRawFreeForgeSquareShaftDrawer.Draw(context.Ctx, context.OriginalBag ?? context.Bag, context.Center, scaleFactor);
}
else
{
DrawShaftFeatures(context);
}
}
else if (IsBlock(structuralFeature))
{
DrawBlockFeatures(context);
}
}
public static void DrawRingRollingWithUnspecifiedFillet(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));
if (!IsRing(structuralFeature)
|| string.IsNullOrWhiteSpace(processMethod)
|| processMethod.IndexOf("轧制", StringComparison.OrdinalIgnoreCase) < 0)
{
Draw(ctx, bag, deliveryStatus, structuralFeature, specialCondition, processMethod, center, scaleFactor);
return;
}
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 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)
};
var outerDia = effectiveBag.GetDoubleOrNull(KeyOuterDiameter1);
var height = effectiveBag.GetDoubleOrNull(KeyHeight1);
if (!outerDia.HasValue || outerDia.Value <= 0 || !height.HasValue || height.Value <= 0)
{
return;
}
DrawRingFeaturesHalfWithUnspecifiedFillet(context, outerDia.Value, height.Value);
}
#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)
{
DrawRingFeaturesHalfCore(ctx, outerDia, height, false);
}
private static void DrawRingFeaturesHalfWithUnspecifiedFillet(DrawingContext ctx, double outerDia, double height)
{
DrawRingFeaturesHalfCore(ctx, outerDia, height, true);
}
private static void DrawRingFeaturesHalfCore(DrawingContext ctx, double outerDia, double height, bool applyUnspecifiedFillet)
{
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: 锻件外轮廓 ===
var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
// 判断是否有圆角
bool hasUnspecifiedFillet = applyUnspecifiedFillet && unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0;
if (!hasUnspecifiedFillet)
{
// 只有在非圆角模式下才绘制单独的外轮廓
// 在圆角模式下,整个实体轮廓由后面的 DrawRingSectionContourWithFillet 统一绘制
DrawForgingOuterContourHalf(ctx, ox, oy, visualOuterR, H, null);
}
// === 特征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);
// 判断是否有圆角 (hasUnspecifiedFillet already declared above)
if (hasUnspecifiedFillet)
{
// 计算圆角半径逻辑,与 DrawRingSectionContourWithFillet 保持一致
var sectionWidth = visualOuterR * 2.0; // Wait, section width is visualOuterR - visualInnerRight? No.
// Calculate visual width for section
var sWidth = (ox + visualOuterR) - (ox + visualInnerR);
var maxR = Math.Min(sWidth * 0.5, H * 0.5);
var rawR = unspecifiedFillet.Value;
var r = Math.Min(rawR, maxR);
double dimOffset = (r > 0.01) ? r : 0.0;
// 新逻辑:如果启用了圆角,直接绘制一个闭合的圆角矩形作为实体轮廓
// 替代原来分散的 外轮廓 + 内孔竖线 + 圆角 的画法,解决直角残留和不平滑问题
DrawRingSectionContourWithFillet(ctx, xInnerRight.Value, ox + visualOuterR, oy, H, unspecifiedFillet.Value);
// 补充:绘制连接中轴线到实体内边缘的上下两条横线(内孔顶底面)
DrawHoleHorizontalLines(ctx, ox, xInnerRight.Value, oy, H, unspecifiedFillet.Value);
// 标注依然需要
// 用户反馈:下边的标注线在竖直方向上不要闭合(保持在底边水平),不要高于白色底部边框
// 修正:用户最新反馈(2026-01-21)要求“在竖直方向上,再向上闭合”,即连接到剖面的左下和右下角(圆角切点)
var dimExtensionOffset = 0.0;
if (r > 0.01) dimExtensionOffset = r;
DrawInnerDiameterDimensionHalf(ctx, ox, xInnerRight.Value, oy, H, innerDia.Value, innerTolPlus, innerTolMinus, innerDiaPrime, dimExtensionOffset);
// 剖面填充
// 剖面填充
DrawRingSectionHatchWithFillet(ctx, xInnerRight.Value, ox + visualOuterR, oy, H, unspecifiedFillet.Value);
}
else
{
// 原有逻辑:直角模式
DrawInnerHoleHalf(ctx, ox, xInnerRight.Value, oy, H);
DrawInnerDiameterDimensionHalf(ctx, ox, xInnerRight.Value, oy, H, innerDia.Value, innerTolPlus, innerTolMinus, innerDiaPrime);
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);
// 计算高度标注的内缩进量(针对圆角闭合)
// 使标注界线的起点X坐标向内缩进 r,从而搭在圆角的切点上,而Y坐标保持不变(与锻件顶底对齐)
double heightDimIndentX = 0.0;
if (hasUnspecifiedFillet)
{
var sWidth = (ox + visualOuterR) - (ox + visualInnerR);
var maxR = Math.Min(sWidth * 0.5, H * 0.5);
var r = Math.Min(unspecifiedFillet.Value, maxR);
if (r > 0.01) heightDimIndentX = r;
}
DrawHeightDimensionHalf(ctx, ox, oy, visualOuterR, H, heightTolPlus, heightTolMinus, heightPrime, heightDimIndentX);
// === 特征6: 未注圆角 ===
// 针对“毛料态-轧制-环形”模板(applyUnspecifiedFillet=true),不显示此文字
if (!applyUnspecifiedFillet && 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)
{
// 同样应用闭合逻辑
var dimExtensionOffset = 0.0;
// 注意这里需要重新获取一次r,或者复用上面的逻辑。
// 简单起见,如果 applyUnspecifiedFillet 为真,则计算 r
if (applyUnspecifiedFillet && unspecifiedFillet.HasValue)
{
// Recalculate r
var sWidth = Math.Abs((ox + visualOuterR) - (xInnerRight.Value));
var maxR = Math.Min(sWidth * 0.5, H * 0.5);
var rCalc = Math.Min(unspecifiedFillet.Value, maxR);
if (rCalc > 0.01) dimExtensionOffset = rCalc;
}
DrawMinWallThicknessNote(ctx, xInnerRight.Value, ox + visualOuterR, oy, minWallThickness.Value, dimExtensionOffset);
}
// === 特征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 markingText = bag.GetString(KeyMarkingContent);
// 只要有标刻内容,或者处于"毛料态-轧制-环形"模式,都画
if (!string.IsNullOrWhiteSpace(markingText) || applyUnspecifiedFillet)
{
// [修改]
// 指向锻件顶部白色边框的中间位置
// 顶部中间 X = (xInnerRight + xOuterRight) / 2
// Y = Top = oy + H
var xLoopInner = ox + visualInnerR;
var xLoopOuter = ox + visualOuterR;
var xTarget = (xLoopInner + xLoopOuter) / 2.0;
var yTarget = oy + H;
if (string.IsNullOrWhiteSpace(markingText))
{
markingText = "HB5936-13"; // 默认值
}
DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText);
}
else
{
// 原有逻辑:指向锻件内孔右上角
var scaledInnerDia = visualInnerR * 2.0;
DrawSpecialInnerHoleLeader(ctx, ox, oy, H, scaledInnerDia);
}
}
// === 特征9: 硬度符号 ===
// 只要有硬度参数就画,不再局限于"毛料态-轧制-环形"
var hardnessVal = bag.GetString(KeyHardness);
if (!string.IsNullOrWhiteSpace(hardnessVal))
{
// 指向锻件剖面图的底部边框的中间位置
// 坐标: mid X, yBottom
double xLoopInner = ox + visualInnerR;
double xLoopOuter = ox + visualOuterR;
double xStart = (xLoopInner + xLoopOuter) / 2.0;
double yStart = oy;
DrawHardnessSymbol(ctx, xStart, yStart);
}
}
///
/// 轧制:绘制带圆角的实体闭合轮廓(圆角多段线)
/// 替代原来分散的外轮廓线和内孔边界线
///
public static void DrawRingSectionContourWithFillet(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); // 90度圆弧的凸度
// 逆时针绘制圆角矩形
// 顺序:左下圆弧 -> 右下圆弧 -> 右上圆弧 -> 左上圆弧
// 下方边:从左下到右下
// 起点 (xLeft + r, yBottom) -> 直线 -> (xRight - r, yBottom) -> 圆弧 -> (xRight, yBottom + r)
var polyFillet = new Polyline();
// 1. 下边直线段起点
polyFillet.AddVertexAt(0, new Point2d(xLeft + r, yBottom), 0, 0, 0);
// 2. 右下角圆弧起点
polyFillet.AddVertexAt(1, new Point2d(xRight - r, yBottom), bulge, 0, 0);
// 3. 右边直线段起点 (圆弧终点)
polyFillet.AddVertexAt(2, new Point2d(xRight, yBottom + r), 0, 0, 0);
// 4. 右上角圆弧起点
polyFillet.AddVertexAt(3, new Point2d(xRight, yTop - r), bulge, 0, 0);
// 5. 上边直线段起点 (圆弧终点)
polyFillet.AddVertexAt(4, new Point2d(xRight - r, yTop), 0, 0, 0);
// 6. 左上角圆弧起点
polyFillet.AddVertexAt(5, new Point2d(xLeft + r, yTop), bulge, 0, 0);
// 7. 左边直线段起点 (圆弧终点)
polyFillet.AddVertexAt(6, new Point2d(xLeft, yTop - r), 0, 0, 0);
// 8. 左下角圆弧起点
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);
}
public static void DrawHoleHorizontalLines(DrawingContext ctx, double xAxis, double xSectionLeft, double yBottom, double height, double filletR)
{
var yTop = yBottom + height;
// 计算延伸量:为了与圆角闭合,横线需要延伸到圆角圆心的X坐标处
// 或者简单点,让它延伸到 (xSectionLeft + r),即圆角的结束位置
var width = 200.0; // dummy big enough width for calculation
var maxR = Math.Min(width * 0.5, height * 0.5);
var r = Math.Min(filletR, maxR);
// 如果圆角很小,就不用延伸太远,但考虑到视觉闭合,应该延伸到 xSectionLeft 就够了?
// 不, 截图显示圆角导致内孔竖直边向内缩进,所以横线必须向右延伸穿过圆角区域,连接到实体。
// 圆角起点的X坐标是 xSectionLeft,圆角终点的X坐标是 xSectionLeft + r
// 实际上,横线应该连接到圆角圆弧的切点?不,横线是顶面/底面,所以应该连接到 (xSectionLeft + r)
double xTarget = xSectionLeft;
if (r > 0.01)
{
xTarget = xSectionLeft + r;
}
// Bottom line
var line1 = new Line(new Point3d(xAxis, yBottom, 0), new Point3d(xTarget, yBottom, 0));
ctx.Style?.Apply(line1, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(line1);
ctx.Tr.AddNewlyCreatedDBObject(line1, true);
// Top line
var line2 = new Line(new Point3d(xAxis, yTop, 0), new Point3d(xTarget, yTop, 0));
ctx.Style?.Apply(line2, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(line2);
ctx.Tr.AddNewlyCreatedDBObject(line2, true);
}
///
/// 自由锻工艺 - 画全图
///
///
/// 自由锻工艺 - 画全图
///
public 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: 硬度符号 ===
var hardnessVal = bag.GetString(KeyHardness);
if (!string.IsNullOrWhiteSpace(hardnessVal))
{
// 指向剖面图的底部边框的中间位置
if (xInnerRight.HasValue)
{
double xStart = (xInnerRight.Value + ox + W) / 2.0;
double yStart = oy;
DrawHardnessSymbol(ctx, xStart, yStart);
}
else
{
// 若无内孔(理论上环形应有),可指向整体中间
DrawHardnessSymbol(ctx, ox + W / 2.0, oy);
}
}
// === 特征10: 标刻内容 ===
var markingText = bag.GetString(KeyMarkingContent);
if (!string.IsNullOrWhiteSpace(markingText))
{
if (xInnerRight.HasValue)
{
double xTarget = (xInnerRight.Value + ox + W) / 2.0;
double yTarget = oy + H;
DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText);
}
else
{
DrawSpecialHBLeaderToTop(ctx, ox + W / 2.0, oy + H, markingText);
}
}
}
public 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
}
}
public 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);
}
public 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, double? filletRadius)
{
var rawR = filletRadius.GetValueOrDefault();
if (rawR > 0)
{
var maxR = Math.Min(radius * 0.5, height * 0.5);
var r = Math.Min(rawR, maxR);
if (r > 0.01)
{
var bulge = Math.Tan(Math.PI / 8.0);
var polyFillet = new Polyline();
polyFillet.AddVertexAt(0, new Point2d(ox, oy), 0, 0, 0);
polyFillet.AddVertexAt(1, new Point2d(ox + radius - r, oy), bulge, 0, 0);
polyFillet.AddVertexAt(2, new Point2d(ox + radius, oy + r), 0, 0, 0);
polyFillet.AddVertexAt(3, new Point2d(ox + radius, oy + height - r), bulge, 0, 0);
polyFillet.AddVertexAt(4, new Point2d(ox + radius - r, oy + height), 0, 0, 0);
polyFillet.AddVertexAt(5, new Point2d(ox, oy + height), 0, 0, 0);
polyFillet.Closed = false;
ctx.Style?.Apply(polyFillet, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(polyFillet);
ctx.Tr.AddNewlyCreatedDBObject(polyFillet, true);
return;
}
}
// 右半边轮廓:下边、右边、上边(左边开口)
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, double offsetY = 0)
{
// 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 + offsetY, 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 DrawInnerFilletsHalfInward(DrawingContext ctx, double xInnerRight, double yBottom, double height, double filletR)
{
var yTop = yBottom + height;
// 仅绘制内孔右侧边界的圆角,圆角朝向剖面内部(右侧)
DrawFilletArc(ctx, xInnerRight + filletR, yTop - filletR, filletR, Math.PI / 2, Math.PI);
DrawFilletArc(ctx, xInnerRight + filletR, yBottom + filletR, filletR, Math.PI, Math.PI * 1.5);
}
///
/// 轧制:高度标注(在右侧)
/// indentX: 界线起点向内横向缩进量(配合圆角使用,使界线起于圆角切点)
///
private static void DrawHeightDimensionHalf(DrawingContext ctx, double ox, double oy, double radius,
double height, double? tolPlus, double? tolMinus, double? heightPrime, double indentX = 0)
{
// 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;
// 界线起点:
// 下端: (ox + radius - indentX, oy)
// 上端: (ox + radius - indentX, oy + height)
// 这样保持了高度(Y)不变,和白色横线对齐,但X方向向内缩进搭在圆角顶点上
var xLine = ox + radius - indentX;
AddLinearDim(
ctx,
new Point3d(xLine, oy, 0),
new Point3d(xLine, 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
public 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);
}
public 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);
}
public 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
{
// 忽略圆弧创建错误
}
}
public 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);
}
public 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);
}
public 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);
}
public 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
{
// 忽略
}
}
public static void DrawMinWallThicknessNote(DrawingContext ctx, double xInnerRight, double xOuterRight, double yBottom, double thickness, double offsetY = 0)
{
var val = ctx.OriginalBag?.GetDoubleOrNull(KeyMinWallThickness) ?? thickness;
var dimText = $"{FormatDimNumber(val)}min";
AddLinearDim(
ctx,
new Point3d(xInnerRight, yBottom + offsetY, 0),
new Point3d(xOuterRight, yBottom + offsetY, 0),
new Point3d((xInnerRight + xOuterRight) / 2, yBottom - 10, 0),
0,
dimText);
}
public 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);
}
}
public 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
}
}
public static void DrawRingSectionHatchWithFillet(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)
{
DrawRingSectionHatch(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
{
// ignore
}
}
catch
{
// ignore
}
}
public 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 辅助方法
public 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 { }
}
public 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
public 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
}
}
public static void DrawSpecialHBLeaderToTop(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);
// 终点:水平向左
// 长度根据文字长度适配,大致估算
// 假设每个字符宽 2.5 (Height 3.5 * 0.7 roughly)
double textLen = (string.IsNullOrEmpty(textContent) ? 10 : textContent.Length) * 2.5;
if (textLen < 20) textLen = 20; // min length
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
{
// ignore
}
}
public static void DrawHardnessSymbol(DrawingContext ctx, double xStart, double yStart)
{
try
{
// 1. 引线 (Diagonal Down-Right -> Horizontal)
// 起点: (xStart, yStart)
// 转折点: (xStart + 15, yStart - 15)
// 终点(圆左侧): (xStart + 15 + 10, yStart - 15)
var p1 = new Point3d(xStart, yStart, 0);
var p2 = new Point3d(xStart + 15, yStart - 15, 0);
var p3 = new Point3d(p2.X + 10, p2.Y, 0); // 这里的p3是直线的终点,圆接在这里
// 绘制引线
var poly = new Polyline();
poly.AddVertexAt(0, new Point2d(p1.X, p1.Y), 0, 0, 0);
poly.AddVertexAt(1, new Point2d(p2.X, p2.Y), 0, 0, 0);
poly.AddVertexAt(2, new Point2d(p3.X, p3.Y), 0, 0, 0);
ctx.Style?.Apply(poly, DrawingStyleManager.Role.Dimension); // 使用 Dimension 角色
// 用户要求改为绿色 (ColorIndex 3)
poly.ColorIndex = 3;
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
// 2. 圆圈 (HB)
// 圆心 x = p3.X + R
double radius = 4.0;
var center = new Point3d(p3.X + radius, p3.Y, 0);
var circle = new Circle(center, Vector3d.ZAxis, radius);
circle.ColorIndex = 3;
ctx.Btr.AppendEntity(circle);
ctx.Tr.AddNewlyCreatedDBObject(circle, true);
// 3. 文字 (HB)
var text = new DBText();
text.TextString = "HB";
text.Height = 3.5;
text.ColorIndex = 3;
text.HorizontalMode = TextHorizontalMode.TextCenter;
text.VerticalMode = TextVerticalMode.TextVerticalMid;
text.AlignmentPoint = center;
text.Position = center;
ctx.Btr.AppendEntity(text);
ctx.Tr.AddNewlyCreatedDBObject(text, true);
}
catch
{
// ignore
}
}
///
/// [MOVED] Logic moved to BlockRawFreeForgeRoundHeadDrawer.cs
///
[Obsolete("Use BlockRawFreeForgeRoundHeadDrawer.Draw instead")]
public static void DrawBlockRawFreeForgeRoundHead(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 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(DrawingContext ctx)
{
var bag = ctx.Bag;
// BoxSize1 (Width - 总长度)
// BoxSize2 (Height - 高度/直径)
var width = bag.GetDoubleOrNull(KeyBoxSize1);
var height = bag.GetDoubleOrNull(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(KeyBoxRoundHeadFilletRadiusMax);
double headFilletR = headFilletParam.HasValue && headFilletParam.Value > 0
? headFilletParam.Value
: 0;
// 获取内框圆角参数:优先取 UnspecifiedFilletRadiusMax,如果没有则取 BoxFilletRadiusMax - 用于内部剖面
var innerFilletParam = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
if (!innerFilletParam.HasValue || innerFilletParam.Value <= 0)
{
innerFilletParam = bag.GetDoubleOrNull(KeyBoxFilletRadiusMax);
}
double sectionFilletR = innerFilletParam.HasValue && innerFilletParam.Value > 0
? innerFilletParam.Value
: 0;
// 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧)
// 修正:圆弧弦线需向内缩进 headFilletR (原 logic 使用 innerFilletR),以与锻件外侧圆角起点对齐
DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, headFilletR);
// 2. 绘制分段式内框(左、中、右三段)以支持中间剖面区域的独立圆角
// 中间区域(剖面)作为主体,如果设置了圆角,四个角都应为圆角
// 计算视觉上的剖面宽度
// 依据用户需求:尺寸3 (BoxSize3) 对应剖面的宽
var size3 = bag.GetDoubleOrNull(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);
// 绘制左侧框线(连接左端圆弧和中间剖面框)
// 左端: xInnerLeft (需处理左上下角的圆角 - 使用 headFilletR)
// 右端: xSectionLeft (连接到中间框的直边)
DrawBoxSideFrame(ctx, xInnerLeft, xSectionLeft, oy, H, headFilletR, isLeft: true);
// 绘制右侧框线
// 左端: xSectionRight (连接到中间框的直边)
// 右端: xInnerRight (需处理右上下角的圆角 - 使用 headFilletR)
DrawBoxSideFrame(ctx, xSectionRight, xInnerRight, oy, H, headFilletR, isLeft: 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(KeyBoxSize1Prime);
var heightPrime = bag.GetDoubleOrNull(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(KeyBoxSize1) ?? W;
var str = ctx.OriginalBag?.GetString(KeyBoxSize1);
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize1TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize1TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(KeyBoxSize1TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(KeyBoxSize1TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方,用括号包裹
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize1Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})";
}
// 修正:标注需涵盖内框宽度 (矩形部分)
// 修正:引线起点改为内框边界 (xInnerLeft/Right) 和高度底部上移圆角半径,确保闭合到白色线框
// 使用 headFilletR 作为 Offset,因为这是侧边框的倒角
double dimOriginY = oy + headFilletR;
AddLinearDim(
ctx,
new Point3d(xInnerLeft, dimOriginY, 0),
new Point3d(xInnerRight, dimOriginY, 0),
new Point3d((xInnerLeft + xInnerRight) / 2, oy - 25, 0),
0,
dimText);
}
// 7. 高度标注 (BoxSize2) - 位于右侧
// 标注从锻件顶部到底部
{
var val = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize2) ?? H;
var str = ctx.OriginalBag?.GetString(KeyBoxSize2);
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize2TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize2TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(KeyBoxSize2TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(KeyBoxSize2TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize2Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({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(KeyBoxSize3) ?? 0;
var str = ctx.OriginalBag?.GetString(KeyBoxSize3);
// 只有当有值时才标注
if (val > 0)
{
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize3TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize3TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(KeyBoxSize3TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(KeyBoxSize3TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(KeyBoxSize3Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})";
}
AddLinearDim(
ctx,
new Point3d(xSectionLeft, oy + H, 0),
new Point3d(xSectionRight, oy + H, 0),
new Point3d((xSectionLeft + xSectionRight) / 2, oy + H + 15, 0),
0,
dimText);
}
}
// 9. 硬度符号 - 从剖面底部引出
var hardnessVal = bag.GetString(KeyHardness);
if (!string.IsNullOrWhiteSpace(hardnessVal))
{
double xStart = (xSectionLeft + xSectionRight) / 2.0;
double yStart = oy;
DrawHardnessSymbol(ctx, xStart, yStart);
}
// 10. 标刻内容引线 - 从剖面顶部引出
var markingText = bag.GetString(KeyMarkingContent);
if (!string.IsNullOrWhiteSpace(markingText))
{
double xTarget = (xSectionLeft + xSectionRight) / 2.0;
double yTarget = oy + H;
DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText);
}
}
///
/// 绘制方体零件轮廓(黄色双点划线矩形)
///
private static void DrawBlockPartContour(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
{
// ignore
}
}
///
/// 绘制白色矩形内框(锻件内部区域边界,四角带圆角)
///
private static void DrawBlockInnerFrameWithFillet(DrawingContext ctx, double x, double y, double width, double height, double filletR)
{
try
{
// 限制圆角半径不超过宽度或高度的一半
double maxR = Math.Min(width / 2.0, height / 2.0);
double r = Math.Min(filletR, maxR);
if (r <= 0.01)
{
// 没有圆角,绘制普通矩形
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.OutlineBold);
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
return;
}
// 绘制带圆角的矩形
// 使用 Polyline 的 bulge 值来绘制圆角
// bulge = tan(角度/4),对于90度圆角,bulge = tan(22.5°) ≈ 0.414
double bulge = Math.Tan(Math.PI / 8.0);
var polyFillet = new Polyline();
// 从左下角开始,顺时针绘制(与其他模板保持一致)
// 左下角圆角起点
polyFillet.AddVertexAt(0, new Point2d(x, y + r), bulge, 0, 0); // 左下圆角
polyFillet.AddVertexAt(1, new Point2d(x + r, y), 0, 0, 0); // 左下圆角结束
// 右下角
polyFillet.AddVertexAt(2, new Point2d(x + width - r, y), bulge, 0, 0); // 右下圆角
polyFillet.AddVertexAt(3, new Point2d(x + width, y + r), 0, 0, 0); // 右下圆角结束
// 右上角
polyFillet.AddVertexAt(4, new Point2d(x + width, y + height - r), bulge, 0, 0); // 右上圆角
polyFillet.AddVertexAt(5, new Point2d(x + width - r, y + height), 0, 0, 0); // 右上圆角结束
// 左上角
polyFillet.AddVertexAt(6, new Point2d(x + r, y + height), bulge, 0, 0); // 左上圆角
polyFillet.AddVertexAt(7, new Point2d(x, y + height - r), 0, 0, 0); // 左上圆角结束
polyFillet.Closed = true;
ctx.Style?.Apply(polyFillet, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(polyFillet);
ctx.Tr.AddNewlyCreatedDBObject(polyFillet, true);
}
catch
{
// ignore
}
}
///
/// 绘制方体有圆头的完整外轮廓
/// 结构:4顶点多段线 (底边直 -> 左侧整体大圆弧 -> 顶边直 -> 右侧整体大圆弧)
/// 修复:
/// 1. 起点回归到水平线端点 (xLayoutLeft + r),解决"起点往下"问题
/// 2. 凸度动态计算,使得圆弧顶点顶到最外侧 (ox),解决"穿模"问题
///
private static void DrawBlockRoundHeadOutline(DrawingContext ctx, double ox, double oy, double totalWidth, double height, double arcHeight, double innerFilletR)
{
try
{
// 1. 基础参数
double H = height;
double r = innerFilletR;
// 弦线X坐标 (内框水平直边结束的位置)
// 原有的布局边界 (用于内孔定位) 是 ox + arcHeight
// 现在的起点要再往里缩 r
double xCurrentLeft = ox + arcHeight + r;
double xCurrentRight = ox + totalWidth - arcHeight - r;
// 2. 凸度计算
// 我们希望圆弧从 xCurrentLeft 开始,顶点到达 ox
// 拱高 Sagitta = xCurrentLeft - ox = (ox + arcHeight + r) - ox = arcHeight + r
double sagitta = arcHeight + r;
double halfChord = H / 2.0;
// 向外凸 (For CW path: Left side Up -> Negative, Right side Down -> Negative)
double bulge = 0;
if (halfChord > 1e-3)
{
bulge = -(sagitta / halfChord);
}
var poly = new Polyline();
// 顺时针绘制 (CW)
// Vertex 0: 底边右侧起点
poly.AddVertexAt(0, new Point2d(xCurrentRight, oy), 0, 0, 0);
// Vertex 1: 底边左侧终点 (左侧大圆弧起点)
// 设置凸度,下一段(到Vertex 2)是圆弧
poly.AddVertexAt(1, new Point2d(xCurrentLeft, oy), bulge, 0, 0);
// Vertex 2: 顶边左侧起点 (左侧大圆弧终点)
poly.AddVertexAt(2, new Point2d(xCurrentLeft, oy + H), 0, 0, 0);
// Vertex 3: 顶边右侧终点 (右侧大圆弧起点)
poly.AddVertexAt(3, new Point2d(xCurrentRight, oy + H), bulge, 0, 0);
// Close back to Vertex 0 (Right Side Arc ends at Vertex 0)
poly.Closed = true;
ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
}
catch
{
// ignore
}
}
///
/// 绘制方体剖面填充(矩形区域的 ANSI31 网格线填充)
///
private static void DrawBlockSectionHatch(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
{
// ignore
}
}
catch
{
// ignore
}
}
///
/// 绘制中间剖面框(带圆角)
/// 逻辑基本照搬 DrawRingSectionContourWithFillet
///
private static void DrawBoxSectionContourWithFillet(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); // 90度圆弧的凸度
// 逆时针绘制圆角矩形
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);
}
///
/// 绘制左侧或右侧的框线
/// isLeft=true: 绘制左侧部分 (xStart=xInnerLeft, xEnd=xSectionLeft)
/// isLeft=false: 绘制右侧部分 (xStart=xSectionRight, xEnd=xInnerRight)
///
private static void DrawBoxSideFrame(DrawingContext ctx, double xStart, double xEnd, double yBottom, double height, double filletR, bool isLeft)
{
var yTop = yBottom + height;
var width = Math.Abs(xEnd - xStart);
if (width < 0.1) return;
var maxR = Math.Min(width * 0.5, height * 0.5);
var r = Math.Min(filletR, maxR);
// 路径是顺时针(CW)绘制的,所以外凸圆角的Bulge应该是负值
var bulge = -Math.Tan(Math.PI / 8.0);
var poly = new Polyline();
if (isLeft)
{
// Left Frame: CW Path
// Start: Bottom-Right (connect to middle) -> Bottom-Left -> Top-Left -> Top-Right (connect to middle)
// P0: xEnd, yBottom
poly.AddVertexAt(0, new Point2d(xEnd, yBottom), 0, 0, 0);
if (r > 0.01)
{
// P1: xStart + r, yBottom (Start of Corner 1) -> Set Bulge here
poly.AddVertexAt(1, new Point2d(xStart + r, yBottom), bulge, 0, 0);
// P2: xStart, yBottom + r (End of Corner 1, Start of Vertical)
poly.AddVertexAt(2, new Point2d(xStart, yBottom + r), 0, 0, 0);
// P3: xStart, yTop - r (End of Vertical, Start of Corner 2) -> Set Bulge here
poly.AddVertexAt(3, new Point2d(xStart, yTop - r), bulge, 0, 0);
// P4: xStart + r, yTop (End of Corner 2, Start of Top Horizontal)
poly.AddVertexAt(4, new Point2d(xStart + r, yTop), 0, 0, 0);
// P5: xEnd, yTop (End)
poly.AddVertexAt(5, new Point2d(xEnd, yTop), 0, 0, 0);
}
else
{
poly.AddVertexAt(1, new Point2d(xStart, yBottom), 0, 0, 0);
poly.AddVertexAt(2, new Point2d(xStart, yTop), 0, 0, 0);
poly.AddVertexAt(3, new Point2d(xEnd, yTop), 0, 0, 0);
}
poly.Closed = false;
}
else
{
// Right Frame: CW Path
// Start: Top-Left (connect to middle) -> Top-Right -> Bottom-Right -> Bottom-Left (connect to middle)
// P0: xStart, yTop
poly.AddVertexAt(0, new Point2d(xStart, yTop), 0, 0, 0);
if (r > 0.01)
{
// P1: xEnd - r, yTop (Start of Corner 1) -> Set Bulge here
poly.AddVertexAt(1, new Point2d(xEnd - r, yTop), bulge, 0, 0);
// P2: xEnd, yTop - r (End of Corner 1, Start of Vertical)
poly.AddVertexAt(2, new Point2d(xEnd, yTop - r), 0, 0, 0);
// P3: xEnd, yBottom + r (End of Vertical, Start of Corner 2) -> Set Bulge here
poly.AddVertexAt(3, new Point2d(xEnd, yBottom + r), bulge, 0, 0);
// P4: xEnd - r, yBottom (End of Corner 2, Start of Bottom Horizontal)
poly.AddVertexAt(4, new Point2d(xEnd - r, yBottom), 0, 0, 0);
// P5: xStart, yBottom (End)
poly.AddVertexAt(5, new Point2d(xStart, yBottom), 0, 0, 0);
}
else
{
poly.AddVertexAt(1, new Point2d(xEnd, yTop), 0, 0, 0);
poly.AddVertexAt(2, new Point2d(xEnd, yBottom), 0, 0, 0);
poly.AddVertexAt(3, new Point2d(xStart, yBottom), 0, 0, 0);
}
poly.Closed = false;
}
ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
}
}
}