CadParamPluging/Cad/FeatureDrivenDrawer.cs

3483 lines
151 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using CadParamPluging.Common;
namespace CadParamPluging.Cad
{
/// <summary>
/// 特征驱动绘图引擎 - 根据参数存在性动态绘制图形
/// </summary>
public static class FeatureDrivenDrawer
{
#region Key常量
// 环形参数
public const string KeyOuterDiameter1 = "OuterDiameter1";
public const string KeyOuterDiameter1TolPlus = "OuterDiameter1TolPlus";
public const string KeyOuterDiameter1TolMinus = "OuterDiameter1TolMinus";
public const string KeyInnerDiameter2 = "InnerDiameter2";
public const string KeyInnerDiameter2TolPlus = "InnerDiameter2TolPlus";
public const string KeyInnerDiameter2TolMinus = "InnerDiameter2TolMinus";
public const string KeyHeight1 = "Height1";
public const string KeyHeight1TolPlus = "Height1TolPlus";
public const string KeyHeight1TolMinus = "Height1TolMinus";
public const string KeyMinWallThickness = "MinWallThickness";
public const string KeyUnspecifiedFilletRadiusMax = "UnspecifiedFilletRadiusMax"; // 环形未注圆角
public const string KeyInnerRadiusMax = "InnerRadiusMax"; // 内径半径R≤中心冲孔
// 环形零件尺寸(车加工态)
public const string KeyOuterDiameter1Prime = "OuterDiameter1Prime";
public const string KeyInnerDiameter2Prime = "InnerDiameter2Prime";
public const string KeyHeight1Prime = "Height1Prime";
// 饼盘参数
public const string KeyDiskDiameter = "DiskDiameter";
public const string KeyDiskDiameterTolPlus = "DiskDiameterTolPlus";
public const string KeyDiskDiameterTolMinus = "DiskDiameterTolMinus";
public const string KeyDiskHeight = "DiskHeight";
public const string KeyDiskHeightTolPlus = "DiskHeightTolPlus";
public const string KeyDiskHeightTolMinus = "DiskHeightTolMinus";
public const string KeyDiskFilletRadiusMax = "DiskFilletRadiusMax"; // 饼盘未注圆角
public const string KeyDiskDiameterPrime = "DiskDiameterPrime"; // 饼盘零件直径
public const string KeyDiskHeightPrime = "DiskHeightPrime"; // 饼盘零件高度
// 轴杆参数(圆轴)
public const string KeyDiameter = "Diameter";
public const string KeyDiameterTolPlus = "DiameterTolPlus";
public const string KeyDiameterTolMinus = "DiameterTolMinus";
public const string KeyLength = "Length";
public const string KeyLengthTolPlus = "LengthTolPlus";
public const string KeyLengthTolMinus = "LengthTolMinus";
public const string KeyShaftFilletRadiusMax = "ShaftFilletRadiusMax"; // 轴杆未注圆角
public const string KeyDiameterPrime = "DiameterPrime"; // 轴杆零件直径
public const string KeyLengthPrime = "LengthPrime"; // 轴杆零件长度
// 方体参数
public const string KeyBoxSize1 = "BoxSize1";
public const string KeyBoxSize1TolPlus = "BoxSize1TolPlus";
public const string KeyBoxSize1TolMinus = "BoxSize1TolMinus";
public const string KeyBoxSize2 = "BoxSize2";
public const string KeyBoxSize2TolPlus = "BoxSize2TolPlus";
public const string KeyBoxSize2TolMinus = "BoxSize2TolMinus";
public const string KeyBoxSize3 = "BoxSize3";
public const string KeyBoxSize3TolPlus = "BoxSize3TolPlus";
public const string KeyBoxSize3TolMinus = "BoxSize3TolMinus";
public const string KeyBoxFilletRadiusMax = "BoxFilletRadiusMax"; // 方体未注圆角
public const string KeyBoxRoundHeadFilletRadiusMax = "BoxRoundHeadFilletRadiusMax"; // 方体圆头处圆角
public const string KeyBoxSize1Prime = "BoxSize1Prime"; // 方体零件尺寸1
public const string KeyBoxSize2Prime = "BoxSize2Prime"; // 方体零件尺寸2
public const string KeyBoxSize3Prime = "BoxSize3Prime"; // 方体零件尺寸3
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;
/// <summary>
/// 是否为轧制工艺(轧制时只画右半边图)
/// </summary>
public bool IsRolling => !string.IsNullOrWhiteSpace(ProcessMethod)
&& ProcessMethod.IndexOf("轧制", StringComparison.OrdinalIgnoreCase) >= 0;
}
#endregion
#region
/// <summary>
/// 预期图形尺寸
/// </summary>
public class ExpectedDrawingSize
{
public double Width { get; set; }
public double Height { get; set; }
public bool IsValid => Width > 0 && Height > 0;
}
/// <summary>
/// 根据参数计算预期绘制图形的尺寸(不包含标注,仅计算图形主体)
/// </summary>
public static ExpectedDrawingSize CalculateExpectedSize(ParamBag bag, string structuralFeature)
{
if (bag == null)
{
return new ExpectedDrawingSize();
}
if (IsRing(structuralFeature))
{
return CalculateRingSize(bag);
}
if (IsDisk(structuralFeature))
{
return CalculateDiskSize(bag);
}
if (IsShaft(structuralFeature))
{
return CalculateShaftSize(bag);
}
if (IsBlock(structuralFeature))
{
return CalculateBlockSize(bag);
}
return new ExpectedDrawingSize();
}
private static ExpectedDrawingSize CalculateRingSize(ParamBag bag)
{
var outerDia = bag.GetDoubleOrNull(KeyOuterDiameter1) ?? 0;
var height = bag.GetDoubleOrNull(KeyHeight1) ?? 0;
// Check if schematic mode applies (Machined + Rolling)
// Need to fetch from bag as we don't have DrawingContext here
var deliveryStatus = bag.GetString("DeliveryStatus");
var processMethod = bag.GetString("ProcessMethod");
bool isMachined = !string.IsNullOrWhiteSpace(deliveryStatus) && deliveryStatus.Contains("车加工");
bool isRolling = !string.IsNullOrWhiteSpace(processMethod) && processMethod.Contains("轧制");
double activeWidth = outerDia;
double activeHeight = height;
var extraMargin = 50.0; // 标注和文字的默认边距双侧共100
if (isMachined && isRolling)
{
var minWallThickness = bag.GetDoubleOrNull(KeyMinWallThickness);
if (minWallThickness.HasValue && minWallThickness.Value > 0)
{
// 示意图模式:视觉宽度 = 最小壁厚 * 3不是物理外径
// 比例 2:1 内孔:实体
activeWidth = minWallThickness.Value * 3.0;
}
else
{
// 降级:视觉宽度 = 高度 * 3
activeWidth = height * 3.0;
}
// 示意图模式下,图形较小,标注边距也相应调整
// 但不要过大,否则会错误触发"超出边框"检测
extraMargin = 60.0; // 示意图的边距稍大于默认值
}
// 环形图形:宽度=视觉宽度X方向高度=高度参数Y方向
// 加上标注的额外空间(双侧)
return new ExpectedDrawingSize
{
Width = activeWidth + extraMargin * 2,
Height = activeHeight + extraMargin * 2
};
}
private static ExpectedDrawingSize CalculateDiskSize(ParamBag bag)
{
var diameter = bag.GetDoubleOrNull(KeyDiskDiameter) ?? 0;
var height = bag.GetDoubleOrNull(KeyDiskHeight) ?? 0;
var extraMargin = 40;
return new ExpectedDrawingSize
{
Width = diameter + extraMargin * 2,
Height = height + extraMargin * 2
};
}
private static ExpectedDrawingSize CalculateShaftSize(ParamBag bag)
{
var diameter = bag.GetDoubleOrNull(KeyDiameter) ?? 0;
var length = bag.GetDoubleOrNull(KeyLength) ?? 0;
var extraMargin = 40;
return new ExpectedDrawingSize
{
Width = length + extraMargin * 2,
Height = diameter + extraMargin * 2
};
}
private static ExpectedDrawingSize CalculateBlockSize(ParamBag bag)
{
var size1 = bag.GetDoubleOrNull(KeyBoxSize1) ?? 0;
var size2 = bag.GetDoubleOrNull(KeyBoxSize2) ?? size1;
var extraMargin = 40;
return new ExpectedDrawingSize
{
Width = size1 + extraMargin * 2,
Height = size2 + extraMargin * 2
};
}
#endregion
#region
/// <summary>
/// 绘制图形(带缩放支持)
/// </summary>
/// <param name="ctx">CAD上下文</param>
/// <param name="bag">参数包</param>
/// <param name="deliveryStatus">交付状态</param>
/// <param name="structuralFeature">结构特征</param>
/// <param name="specialCondition">特殊条件</param>
/// <param name="processMethod">工艺方法(轧制/自由锻)</param>
/// <param name="center">绘图中心点</param>
/// <param name="scaleFactor">缩放比例1.0表示不缩放)</param>
public static void Draw(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature,
string specialCondition = null, string processMethod = null, Point3d? center = null, double scaleFactor = 1.0)
{
if (ctx == null) throw new ArgumentNullException(nameof(ctx));
if (bag == null) throw new ArgumentNullException(nameof(bag));
// 如果需要缩放,创建一个缩放后的参数包
var effectiveBag = scaleFactor < 1.0 ? ScaleParamBag(bag, scaleFactor) : bag;
var db = ctx.Database;
var tr = ctx.Transaction;
// Important: templates & cleanup operate on ModelSpace; draw there to avoid PaperSpace-related dimension issues.
var prevDb = HostApplicationServices.WorkingDatabase;
HostApplicationServices.WorkingDatabase = db;
BlockTableRecord btr;
try
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
}
finally
{
HostApplicationServices.WorkingDatabase = prevDb;
}
var context = new DrawingContext
{
Ctx = ctx,
Bag = effectiveBag,
OriginalBag = bag,
Center = center ?? Point3d.Origin,
DeliveryStatus = deliveryStatus,
StructuralFeature = structuralFeature,
SpecialCondition = specialCondition,
ProcessMethod = processMethod,
Btr = btr,
Style = new DrawingStyleManager(db, tr)
};
// 根据结构特征分发到对应绘制器
if (IsRing(structuralFeature))
{
DrawRingFeatures(context);
}
else if (IsDisk(structuralFeature))
{
DrawDiskFeatures(context);
}
else if (IsShaft(structuralFeature))
{
DrawShaftFeatures(context);
}
else if (IsBlock(structuralFeature))
{
DrawBlockFeatures(context);
}
}
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);
}
}
/// <summary>
/// 轧制工艺 - 只画右半边图
/// </summary>
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);
}
}
/// <summary>
/// 轧制:绘制带圆角的实体闭合轮廓(圆角多段线)
/// 替代原来分散的外轮廓线和内孔边界线
/// </summary>
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);
}
/// <summary>
/// 自由锻工艺 - 画全图
/// </summary>
/// <summary>
/// 自由锻工艺 - 画全图
/// </summary>
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
/// <summary>
/// 轧制:绘制右半边外轮廓(开口多段线)
/// </summary>
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);
}
/// <summary>
/// 轧制绘制截断线两根双点划线间距5mm
/// </summary>
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);
}
/// <summary>
/// 轧制:外径标注(从对称轴到右边,标注在图形下方偏内)
/// </summary>
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);
}
/// <summary>
/// 轧制:只画右侧内孔线
/// </summary>
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);
}
/// <summary>
/// 轧制:内径标注(从对称轴到右边内孔,放到下方并与最小壁厚标注共线)
/// </summary>
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);
}
/// <summary>
/// 轧制:只画右侧内径圆角
/// </summary>
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);
}
/// <summary>
/// 轧制:高度标注(在右侧)
/// indentX: 界线起点向内横向缩进量(配合圆角使用,使界线起于圆角切点)
/// </summary>
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);
}
/// <summary>
/// 轧制:零件轮廓(闭合方框,双点划线黄色)
/// </summary>
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);
}
}
/// <summary>
/// 方轴绘制(使用方体参数)
/// </summary>
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<Dimension, DrawingContext> 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 { }
}
/// <summary>
/// 对参数包中的尺寸参数应用缩放比例
/// </summary>
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<string>(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<string>(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
}
}
/// <summary>
/// 毛料态-自由锻-方体-有圆头:独立图纸生成逻辑
/// 逻辑完全复制自“车加工-轧制-环形”,但映射了方体参数
/// </summary>
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;
// 外轮廓圆弧:优先使用参数 BoxRoundHeadFilletRadiusMax 控制
var roundHeadRParam = bag.GetDoubleOrNull(KeyBoxRoundHeadFilletRadiusMax);
double outerArcR;
if (roundHeadRParam.HasValue && roundHeadRParam.Value > 0)
{
outerArcR = roundHeadRParam.Value;
// 限制最大半径不超过高度的一半(防止自交)
outerArcR = Math.Min(outerArcR, H / 2.0);
}
else
{
// 默认兜底固定为高度的15%作为圆弧半径
outerArcR = H * 0.15;
}
// 视觉比例:总宽度映射
double visualTotalW = H * 3.0; // 保持视觉宽高比约 3:1
// 视觉上的外轮廓圆弧半径
double visualOuterArcR = outerArcR;
// 绘图坐标计算 (保持居中)
double ox = ctx.Center.X - visualTotalW / 2.0; // 左边界
double oy = ctx.Center.Y - H / 2.0; // 底边
// 内框边界(从左圆弧结束到右圆弧开始)
double xInnerLeft = ox + visualOuterArcR;
double xInnerRight = ox + visualTotalW - visualOuterArcR;
double innerWidth = xInnerRight - xInnerLeft;
// 获取内框圆角参数:优先取 UnspecifiedFilletRadiusMax如果没有则取 BoxFilletRadiusMax
var innerFilletParam = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
if (!innerFilletParam.HasValue || innerFilletParam.Value <= 0)
{
innerFilletParam = bag.GetDoubleOrNull(KeyBoxFilletRadiusMax);
}
double innerFilletR = innerFilletParam.HasValue && innerFilletParam.Value > 0
? innerFilletParam.Value
: 0;
// 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,固定小弧度)
// 修正外轮廓圆弧半径需增加内框圆角半径以保证外圆弧的起点与内框圆角的起点在X轴上重合消除直边延伸
DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, visualOuterArcR + innerFilletR);
// 2. 绘制分段式内框(左、中、右三段)以支持中间剖面区域的独立圆角
// 中间区域(剖面)作为主体,如果设置了圆角,四个角都应为圆角
// 计算分段坐标
double sectionWidth = innerWidth / 3.0;
double xSectionLeft = xInnerLeft + sectionWidth;
double xSectionRight = xInnerLeft + sectionWidth * 2.0;
// 绘制中间剖面框(带圆角)
// 复用 Ring 模板的 SectionContourWithFillet 逻辑绘制中间闭合框
// 注意:这里需要把 Ring 的 DrawRingSectionContourWithFillet 方法提取出来通用或者直接复制逻辑
// 为简单起见,这里直接使用类似的逻辑绘制
DrawBoxSectionContourWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR);
// 绘制左侧框线(连接左端圆弧和中间剖面框)
// 左端: xInnerLeft (需处理左上下角的圆角 - 修正为使用内框圆角 innerFilletR与方体参数一致)
// 右端: xSectionLeft (连接到中间框的直边)
DrawBoxSideFrame(ctx, xInnerLeft, xSectionLeft, oy, H, innerFilletR, isLeft: true);
// 绘制右侧框线
// 左端: xSectionRight (连接到中间框的直边)
// 右端: xInnerRight (需处理右上下角的圆角 - 修正为使用内框圆角 innerFilletR与方体参数一致)
DrawBoxSideFrame(ctx, xSectionRight, xInnerRight, oy, H, innerFilletR, isLeft: false);
// 3. 剖面填充 - 只在中间区域
// 如果有圆角填充范围也应考虑圆角不过Hatch边界通常可以自动处理
if (innerFilletR > 0.01)
{
DrawRingSectionHatchWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR);
}
else
{
DrawBlockSectionHatch(ctx, xSectionLeft, xSectionRight, oy, H);
}
// 4. 剖面左右边界线 -> 已包含在 DrawBoxSectionContourWithFillet 中
// 故移除 DrawSectionInnerBoundaryBold 调用
// 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) - 位于底部
// 标注从锻件最左端到最右端(包括圆头)
{
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);
// 标注点从内框左右边界开始(连接到矩形内框)
AddLinearDim(
ctx,
new Point3d(xInnerLeft, oy, 0),
new Point3d(xInnerRight, oy, 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);
// 标注从圆弧最外侧开始(避免与圆弧轮廓线重叠)
double xOuterRight = ox + visualTotalW;
// 修正:标注界线起点应向内缩进(visualOuterArcR + innerFilletR),以连接到锻件顶底平直边的末端
// 否则标注界线会从包围盒的角点开始,导致与圆角轮廓之间有空隙
double xDimOrigin = xOuterRight - (visualOuterArcR + innerFilletR);
AddLinearDim(
ctx,
new Point3d(xDimOrigin, oy, 0),
new Point3d(xDimOrigin, oy + H, 0),
new Point3d(xOuterRight + 20, oy + H / 2, 0),
90,
dimText);
}
// 8. 中间矩形宽度标注(剖面区域宽度)- 第二个宽度标注,位于上方
{
var rectRealWidth = W - 2 * outerArcR; // 实际中间矩形宽度 = 总宽度 - 两端圆头
if (rectRealWidth > 0)
{
AddLinearDim(
ctx,
new Point3d(xSectionLeft, oy + H, 0),
new Point3d(xSectionRight, oy + H, 0),
new Point3d((xSectionLeft + xSectionRight) / 2, oy + H + 15, 0),
0,
FormatDimNumber(rectRealWidth, null));
}
}
// 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);
}
}
/// <summary>
/// 绘制方体零件轮廓(黄色双点划线矩形)
/// </summary>
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
}
}
/// <summary>
/// 绘制白色矩形内框(锻件内部区域边界,四角带圆角)
/// </summary>
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
}
}
/// <summary>
/// 绘制方体有圆头的完整外轮廓
/// 结构:左圆弧 + 中间矩形顶底边 + 右圆弧(小弧度,不是半圆)
/// </summary>
private static void DrawBlockRoundHeadOutline(DrawingContext ctx, double ox, double oy, double totalWidth, double height, double arcR)
{
try
{
// 使用 Polyline 绘制外轮廓
// arcR 是圆弧部分的半径(固定小弧度)
var poly = new Polyline();
// 圆弧的 bulge 值对于90度圆角bulge = tan(22.5°) ≈ 0.414
double bulge = Math.Tan(Math.PI / 8.0);
// 左侧圆弧中心 X 坐标
double xLeftArc = ox + arcR;
// 右侧圆弧中心 X 坐标
double xRightArc = ox + totalWidth - arcR;
// 从左下开始,顺时针绘制带圆角的矩形外轮廓
// 左下角
poly.AddVertexAt(0, new Point2d(ox, oy + arcR), bulge, 0, 0); // 左下圆弧
poly.AddVertexAt(1, new Point2d(xLeftArc, oy), 0, 0, 0); // 底边开始
// 右下角
poly.AddVertexAt(2, new Point2d(xRightArc, oy), bulge, 0, 0); // 右下圆弧
poly.AddVertexAt(3, new Point2d(ox + totalWidth, oy + arcR), 0, 0, 0); // 右边开始
// 右上角
poly.AddVertexAt(4, new Point2d(ox + totalWidth, oy + height - arcR), bulge, 0, 0); // 右上圆弧
poly.AddVertexAt(5, new Point2d(xRightArc, oy + height), 0, 0, 0); // 顶边开始
// 左上角
poly.AddVertexAt(6, new Point2d(xLeftArc, oy + height), bulge, 0, 0); // 左上圆弧
poly.AddVertexAt(7, new Point2d(ox, oy + height - arcR), 0, 0, 0); // 左边开始
poly.Closed = true;
ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
}
catch
{
// ignore
}
}
/// <summary>
/// 绘制方体剖面填充(矩形区域的 ANSI31 网格线填充)
/// </summary>
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
}
}
/// <summary>
/// 绘制中间剖面框(带圆角)
/// 逻辑基本照搬 DrawRingSectionContourWithFillet
/// </summary>
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);
}
/// <summary>
/// 绘制左侧或右侧的框线
/// isLeft=true: 绘制左侧部分 (xStart=xInnerLeft, xEnd=xSectionLeft)
/// isLeft=false: 绘制右侧部分 (xStart=xSectionRight, xEnd=xInnerRight)
/// </summary>
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);
}
}
}