diff --git a/Cad/FeatureDrivenDrawer.cs b/Cad/FeatureDrivenDrawer.cs
new file mode 100644
index 0000000..60e8acf
--- /dev/null
+++ b/Cad/FeatureDrivenDrawer.cs
@@ -0,0 +1,764 @@
+using System;
+using System.Collections.Generic;
+using Autodesk.AutoCAD.DatabaseServices;
+using Autodesk.AutoCAD.Geometry;
+using CadParamPluging.Common;
+
+namespace CadParamPluging.Cad
+{
+ ///
+ /// 特征驱动绘图引擎 - 根据参数存在性动态绘制图形
+ ///
+ public static class FeatureDrivenDrawer
+ {
+ #region 参数Key常量
+
+ // 环形参数
+ public const string KeyOuterDiameter1 = "OuterDiameter1";
+ public const string KeyOuterDiameter1TolPlus = "OuterDiameter1TolPlus";
+ public const string KeyOuterDiameter1TolMinus = "OuterDiameter1TolMinus";
+ public const string KeyInnerDiameter2 = "InnerDiameter2";
+ public const string KeyInnerDiameter2TolPlus = "InnerDiameter2TolPlus";
+ public const string KeyInnerDiameter2TolMinus = "InnerDiameter2TolMinus";
+ public const string KeyHeight1 = "Height1";
+ public const string KeyHeight1TolPlus = "Height1TolPlus";
+ public const string KeyHeight1TolMinus = "Height1TolMinus";
+ public const string KeyMinWallThickness = "MinWallThickness";
+
+ // 零件尺寸(车加工态)
+ public const string KeyOuterDiameter1Prime = "OuterDiameter1Prime";
+ public const string KeyInnerDiameter2Prime = "InnerDiameter2Prime";
+ public const string KeyHeight1Prime = "Height1Prime";
+
+ // 可选特征参数
+ public const string KeyUnspecifiedFilletRadiusMax = "UnspecifiedFilletRadiusMax";
+ public const string KeyInnerRadiusMax = "InnerRadiusMax";
+ public const string KeyRoundHeadFilletRadiusMax = "RoundHeadFilletRadiusMax";
+
+ // 饼盘参数
+ 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 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 KeySize1 = "Size1";
+ public const string KeySize2 = "Size2";
+ public const string KeySize3 = "Size3";
+ public const string KeySize1TolPlus = "Size1TolPlus";
+ public const string KeySize1TolMinus = "Size1TolMinus";
+ public const string KeySize2TolPlus = "Size2TolPlus";
+ public const string KeySize2TolMinus = "Size2TolMinus";
+ public const string KeySize3TolPlus = "Size3TolPlus";
+ public const string KeySize3TolMinus = "Size3TolMinus";
+
+ #endregion
+
+ #region 绘图上下文
+
+ public class DrawingContext
+ {
+ public CadContext Ctx { get; set; }
+ public ParamBag Bag { get; set; }
+ public Point3d Center { get; set; }
+ public string DeliveryStatus { get; set; }
+ public string StructuralFeature { get; set; }
+ public string SpecialCondition { get; set; }
+
+ public Database Db => Ctx?.Database;
+ public Transaction Tr => Ctx?.Transaction;
+ public BlockTableRecord Btr { get; set; }
+
+ public bool IsMachined => !string.IsNullOrWhiteSpace(DeliveryStatus)
+ && DeliveryStatus.IndexOf("车加工", StringComparison.OrdinalIgnoreCase) >= 0;
+
+ public bool IsCenterPunched => !string.IsNullOrWhiteSpace(SpecialCondition)
+ && SpecialCondition.IndexOf("中心冲孔", StringComparison.OrdinalIgnoreCase) >= 0;
+
+ public bool HasRoundHead => !string.IsNullOrWhiteSpace(SpecialCondition)
+ && SpecialCondition.IndexOf("有圆头", StringComparison.OrdinalIgnoreCase) >= 0;
+ }
+
+ #endregion
+
+ #region 主入口
+
+ public static void Draw(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature,
+ string specialCondition = null, Point3d? center = null)
+ {
+ if (ctx == null) throw new ArgumentNullException(nameof(ctx));
+ if (bag == null) throw new ArgumentNullException(nameof(bag));
+
+ var db = ctx.Database;
+ var tr = ctx.Transaction;
+ var btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
+
+ var context = new DrawingContext
+ {
+ Ctx = ctx,
+ Bag = bag,
+ Center = center ?? Point3d.Origin,
+ DeliveryStatus = deliveryStatus,
+ StructuralFeature = structuralFeature,
+ SpecialCondition = specialCondition,
+ Btr = btr
+ };
+
+ // 根据结构特征分发到对应绘制器
+ if (IsRing(structuralFeature))
+ {
+ DrawRingFeatures(context);
+ }
+ else if (IsDisk(structuralFeature))
+ {
+ DrawDiskFeatures(context);
+ }
+ else if (IsShaft(structuralFeature))
+ {
+ DrawShaftFeatures(context);
+ }
+ else if (IsBlock(structuralFeature))
+ {
+ DrawBlockFeatures(context);
+ }
+ }
+
+ #endregion
+
+ #region 结构特征判断
+
+ private static bool IsRing(string feature)
+ {
+ return !string.IsNullOrWhiteSpace(feature)
+ && feature.IndexOf("环形", StringComparison.OrdinalIgnoreCase) >= 0;
+ }
+
+ private static bool IsDisk(string feature)
+ {
+ return !string.IsNullOrWhiteSpace(feature)
+ && (feature.IndexOf("饼盘", StringComparison.OrdinalIgnoreCase) >= 0
+ || feature.IndexOf("饼", StringComparison.OrdinalIgnoreCase) >= 0
+ || feature.IndexOf("盘", StringComparison.OrdinalIgnoreCase) >= 0);
+ }
+
+ private static bool IsShaft(string feature)
+ {
+ return !string.IsNullOrWhiteSpace(feature)
+ && (feature.IndexOf("轴杆", StringComparison.OrdinalIgnoreCase) >= 0
+ || feature.IndexOf("轴", StringComparison.OrdinalIgnoreCase) >= 0
+ || feature.IndexOf("杆", StringComparison.OrdinalIgnoreCase) >= 0);
+ }
+
+ private static bool IsBlock(string feature)
+ {
+ return !string.IsNullOrWhiteSpace(feature)
+ && feature.IndexOf("方体", StringComparison.OrdinalIgnoreCase) >= 0;
+ }
+
+ #endregion
+
+ #region 环形绘制 - 特征驱动
+
+ private static void DrawRingFeatures(DrawingContext ctx)
+ {
+ var bag = ctx.Bag;
+
+ // 必需参数检测
+ var outerDia = bag.GetDoubleOrNull(KeyOuterDiameter1);
+ var height = bag.GetDoubleOrNull(KeyHeight1);
+
+ if (!outerDia.HasValue || outerDia.Value <= 0)
+ {
+ return; // 缺少外径,无法绘制
+ }
+
+ if (!height.HasValue || height.Value <= 0)
+ {
+ return; // 缺少高度,无法绘制
+ }
+
+ // 计算图形偏移,使其居中
+ double ox = ctx.Center.X - height.Value / 2.0;
+ double oy = ctx.Center.Y;
+ double R = outerDia.Value / 2.0;
+
+ // 加载虚线线型
+ TryLoadLinetype(ctx.Db, ctx.Tr, "DASHED");
+
+ // === 特征1: 锻件外轮廓(必绘) ===
+ DrawForgingOuterContour(ctx, ox, oy, R, height.Value);
+
+ // === 特征2: 外径公差标注(如果有公差参数) ===
+ var outerTolPlus = bag.GetDoubleOrNull(KeyOuterDiameter1TolPlus);
+ var outerTolMinus = bag.GetDoubleOrNull(KeyOuterDiameter1TolMinus);
+ DrawOuterDiameterDimension(ctx, ox, oy, R, outerDia.Value, outerTolPlus, outerTolMinus);
+
+ // === 特征3: 内孔(如果有内径参数) ===
+ var innerDia = bag.GetDoubleOrNull(KeyInnerDiameter2);
+ if (innerDia.HasValue && innerDia.Value > 0)
+ {
+ double r = innerDia.Value / 2.0;
+ var innerTolPlus = bag.GetDoubleOrNull(KeyInnerDiameter2TolPlus);
+ var innerTolMinus = bag.GetDoubleOrNull(KeyInnerDiameter2TolMinus);
+
+ DrawInnerHole(ctx, ox, oy, r, height.Value);
+ DrawInnerDiameterDimension(ctx, ox, oy, r, height.Value, innerDia.Value, innerTolPlus, innerTolMinus);
+
+ // === 特征4: 内径圆角(如果有内径半径R参数,且是中心冲孔) ===
+ var innerRadiusMax = bag.GetDoubleOrNull(KeyInnerRadiusMax);
+ if (innerRadiusMax.HasValue && innerRadiusMax.Value > 0 && ctx.IsCenterPunched)
+ {
+ DrawInnerFillets(ctx, ox, oy, r, height.Value, innerRadiusMax.Value);
+ }
+ }
+
+ // === 特征5: 高度/长度标注 ===
+ var heightTolPlus = bag.GetDoubleOrNull(KeyHeight1TolPlus);
+ var heightTolMinus = bag.GetDoubleOrNull(KeyHeight1TolMinus);
+ DrawHeightDimension(ctx, ox, oy, R, height.Value, heightTolPlus, heightTolMinus);
+
+ // === 特征6: 未注圆角(如果有该参数) ===
+ var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
+ if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0)
+ {
+ DrawUnspecifiedFilletNote(ctx, ox, oy, R, height.Value, unspecifiedFillet.Value);
+ }
+
+ // === 特征7: 最小壁厚T标注(如果有该参数) ===
+ var minWallThickness = bag.GetDoubleOrNull(KeyMinWallThickness);
+ if (minWallThickness.HasValue && minWallThickness.Value > 0 && innerDia.HasValue)
+ {
+ DrawMinWallThicknessNote(ctx, ox, oy, R, innerDia.Value / 2.0, minWallThickness.Value);
+ }
+
+ // === 特征8: 零件轮廓(车加工态) ===
+ if (ctx.IsMachined)
+ {
+ var outerDiaPrime = bag.GetDoubleOrNull(KeyOuterDiameter1Prime);
+ var innerDiaPrime = bag.GetDoubleOrNull(KeyInnerDiameter2Prime);
+ var heightPrime = bag.GetDoubleOrNull(KeyHeight1Prime);
+
+ if (outerDiaPrime.HasValue && outerDiaPrime.Value > 0 && heightPrime.HasValue && heightPrime.Value > 0)
+ {
+ DrawPartContour(ctx, ox, oy, height.Value, outerDiaPrime.Value, innerDiaPrime, heightPrime.Value);
+ }
+ }
+ }
+
+ private static void DrawForgingOuterContour(DrawingContext ctx, double ox, double oy, double R, double height)
+ {
+ var poly = new Polyline();
+ poly.AddVertexAt(0, new Point2d(ox, oy - R), 0, 0, 0);
+ poly.AddVertexAt(1, new Point2d(ox + height, oy - R), 0, 0, 0);
+ poly.AddVertexAt(2, new Point2d(ox + height, oy + R), 0, 0, 0);
+ poly.AddVertexAt(3, new Point2d(ox, oy + R), 0, 0, 0);
+ poly.Closed = true;
+ poly.ColorIndex = 7;
+
+ ctx.Btr.AppendEntity(poly);
+ ctx.Tr.AddNewlyCreatedDBObject(poly, true);
+ }
+
+ private static void DrawInnerHole(DrawingContext ctx, double ox, double oy, double r, double height)
+ {
+ var lineTop = new Line(new Point3d(ox, oy + r, 0), new Point3d(ox + height, oy + r, 0));
+ lineTop.ColorIndex = 2;
+ lineTop.Linetype = "DASHED";
+ ctx.Btr.AppendEntity(lineTop);
+ ctx.Tr.AddNewlyCreatedDBObject(lineTop, true);
+
+ var lineBot = new Line(new Point3d(ox, oy - r, 0), new Point3d(ox + height, oy - r, 0));
+ lineBot.ColorIndex = 2;
+ lineBot.Linetype = "DASHED";
+ ctx.Btr.AppendEntity(lineBot);
+ ctx.Tr.AddNewlyCreatedDBObject(lineBot, true);
+ }
+
+ private static void DrawInnerFillets(DrawingContext ctx, double ox, double oy, double r, double height, double filletR)
+ {
+ // 在内孔四角绘制圆角示意
+ // 左上角
+ DrawFilletArc(ctx, ox + filletR, oy + r - filletR, filletR, Math.PI / 2, Math.PI);
+ // 左下角
+ DrawFilletArc(ctx, ox + filletR, oy - r + filletR, filletR, Math.PI, Math.PI * 1.5);
+ // 右上角
+ DrawFilletArc(ctx, ox + height - filletR, oy + r - filletR, filletR, 0, Math.PI / 2);
+ // 右下角
+ DrawFilletArc(ctx, ox + height - filletR, oy - r + filletR, filletR, Math.PI * 1.5, Math.PI * 2);
+ }
+
+ private static void DrawFilletArc(DrawingContext ctx, double cx, double cy, double r, double startAngle, double endAngle)
+ {
+ try
+ {
+ var arc = new Arc(new Point3d(cx, cy, 0), r, startAngle, endAngle);
+ arc.ColorIndex = 7;
+ ctx.Btr.AppendEntity(arc);
+ ctx.Tr.AddNewlyCreatedDBObject(arc, true);
+ }
+ catch
+ {
+ // 忽略圆弧创建错误
+ }
+ }
+
+ private static void DrawOuterDiameterDimension(DrawingContext ctx, double ox, double oy, double R,
+ double diameter, double? tolPlus, double? tolMinus)
+ {
+ var dimText = BuildDimensionText($"%%c{diameter}", tolPlus, tolMinus);
+ AddLinearDim(ctx, new Point3d(ox, oy + R, 0), new Point3d(ox, oy - R, 0),
+ new Point3d(ox - 20, oy, 0), 90, dimText);
+ }
+
+ private static void DrawInnerDiameterDimension(DrawingContext ctx, double ox, double oy, double r,
+ double height, double diameter, double? tolPlus, double? tolMinus)
+ {
+ var dimText = BuildDimensionText($"%%c{diameter}", tolPlus, tolMinus);
+ AddLinearDim(ctx, new Point3d(ox, oy + r, 0), new Point3d(ox, oy - r, 0),
+ new Point3d(ox + height + 20, oy, 0), 90, dimText);
+ }
+
+ private static void DrawHeightDimension(DrawingContext ctx, double ox, double oy, double R,
+ double height, double? tolPlus, double? tolMinus)
+ {
+ var baseText = $"{height}min";
+ var dimText = BuildDimensionText(baseText, tolPlus, tolMinus);
+ AddLinearDim(ctx, new Point3d(ox, oy - R, 0), new Point3d(ox + height, oy - R, 0),
+ new Point3d(ox + height / 2, oy - R - 20, 0), 0, dimText);
+ }
+
+ private static void DrawUnspecifiedFilletNote(DrawingContext ctx, double ox, double oy, double R, double height, double filletR)
+ {
+ // 在图纸右上角添加文字说明
+ try
+ {
+ var text = new DBText();
+ text.TextString = $"未注圆角半径R≤{filletR}";
+ text.Position = new Point3d(ox + height + 10, oy + R + 15, 0);
+ text.Height = 5;
+ text.ColorIndex = 7;
+ ctx.Btr.AppendEntity(text);
+ ctx.Tr.AddNewlyCreatedDBObject(text, true);
+ }
+ catch
+ {
+ // 忽略
+ }
+ }
+
+ private static void DrawMinWallThicknessNote(DrawingContext ctx, double ox, double oy, double R, double r, double thickness)
+ {
+ // 壁厚标注线 - 在截面中间位置
+ try
+ {
+ // 在内外径之间画一条标注线
+ var midX = ox;
+ var midY = oy + (R + r) / 2;
+
+ var leader = new Line(new Point3d(midX, oy + r, 0), new Point3d(midX, oy + R, 0));
+ leader.ColorIndex = 4;
+ ctx.Btr.AppendEntity(leader);
+ ctx.Tr.AddNewlyCreatedDBObject(leader, true);
+
+ var text = new DBText();
+ text.TextString = $"T≥{thickness}";
+ text.Position = new Point3d(midX - 20, midY, 0);
+ text.Height = 4;
+ text.ColorIndex = 4;
+ ctx.Btr.AppendEntity(text);
+ ctx.Tr.AddNewlyCreatedDBObject(text, true);
+ }
+ catch
+ {
+ // 忽略
+ }
+ }
+
+ private static void DrawPartContour(DrawingContext ctx, double ox, double oy, double forgingHeight,
+ double outerDiaPrime, double? innerDiaPrime, double heightPrime)
+ {
+ double offsetX = (forgingHeight - heightPrime) / 2.0;
+ double Rp = outerDiaPrime / 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 + heightPrime, oy - Rp), 0, 0, 0);
+ polyPart.AddVertexAt(2, new Point2d(ox + offsetX + heightPrime, oy + Rp), 0, 0, 0);
+ polyPart.AddVertexAt(3, new Point2d(ox + offsetX, oy + Rp), 0, 0, 0);
+ polyPart.Closed = true;
+ polyPart.ColorIndex = 7;
+
+ ctx.Btr.AppendEntity(polyPart);
+ ctx.Tr.AddNewlyCreatedDBObject(polyPart, true);
+
+ // 填充
+ try
+ {
+ 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 = 0.8;
+ hatch.PatternAngle = 0;
+ hatch.Associative = false;
+ hatch.ColorIndex = 7;
+
+ var ids = new ObjectIdCollection();
+ ids.Add(polyPart.ObjectId);
+ hatch.AppendLoop(HatchLoopTypes.External, ids);
+ hatch.EvaluateHatch(true);
+ }
+ catch
+ {
+ // 忽略
+ }
+
+ // 零件外径标注
+ 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{outerDiaPrime}");
+
+ // 零件高度标注
+ AddLinearDim(ctx, new Point3d(ox + offsetX, oy - Rp, 0), new Point3d(ox + offsetX + heightPrime, oy - Rp, 0),
+ new Point3d(ox + offsetX + heightPrime / 2, oy - Rp - 10, 0), 0, $"{heightPrime}");
+ }
+
+ #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;
+ poly.ColorIndex = 7;
+
+ ctx.Btr.AppendEntity(poly);
+ ctx.Tr.AddNewlyCreatedDBObject(poly, true);
+
+ // 直径标注
+ var diaTolPlus = bag.GetDoubleOrNull(KeyDiskDiameterTolPlus);
+ var diaTolMinus = bag.GetDoubleOrNull(KeyDiskDiameterTolMinus);
+ var diaDimText = BuildDimensionText($"%%c{diameter.Value}", diaTolPlus, diaTolMinus);
+ 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 htDimText = BuildDimensionText($"{H}", htTolPlus, htTolMinus);
+ 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);
+
+ // 未注圆角
+ var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
+ if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0)
+ {
+ DrawUnspecifiedFilletNote(ctx, ox, oy + H, R, diameter.Value, unspecifiedFillet.Value);
+ }
+ }
+
+ #endregion
+
+ #region 轴杆绘制 - 特征驱动
+
+ private static void DrawShaftFeatures(DrawingContext ctx)
+ {
+ var bag = ctx.Bag;
+
+ 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;
+ poly.ColorIndex = 7;
+
+ 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));
+ centerLine.ColorIndex = 1;
+ centerLine.Linetype = "CENTER";
+ ctx.Btr.AppendEntity(centerLine);
+ ctx.Tr.AddNewlyCreatedDBObject(centerLine, true);
+
+ // 直径标注
+ var diaTolPlus = bag.GetDoubleOrNull(KeyDiameterTolPlus);
+ var diaTolMinus = bag.GetDoubleOrNull(KeyDiameterTolMinus);
+ var diaDimText = BuildDimensionText($"%%c{diameter.Value}", diaTolPlus, diaTolMinus);
+ 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 lenDimText = BuildDimensionText($"{L}", lenTolPlus, lenTolMinus);
+ 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);
+
+ // 未注圆角
+ var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
+ if (unspecifiedFillet.HasValue && unspecifiedFillet.Value > 0)
+ {
+ DrawUnspecifiedFilletNote(ctx, ox, oy, R, L, unspecifiedFillet.Value);
+ }
+
+ // 圆头处圆角(如果有圆头)
+ if (ctx.HasRoundHead)
+ {
+ var roundHeadFillet = bag.GetDoubleOrNull(KeyRoundHeadFilletRadiusMax);
+ if (roundHeadFillet.HasValue && roundHeadFillet.Value > 0)
+ {
+ // 绘制圆头
+ DrawRoundHeadEnds(ctx, ox, oy, R, L, roundHeadFillet.Value);
+ }
+ }
+ }
+
+ private static void DrawRoundHeadEnds(DrawingContext ctx, double ox, double oy, double R, double L, double filletR)
+ {
+ // 左端圆头
+ try
+ {
+ var arcLeft = new Arc(new Point3d(ox, oy, 0), R, Math.PI / 2, Math.PI * 1.5);
+ arcLeft.ColorIndex = 7;
+ ctx.Btr.AppendEntity(arcLeft);
+ ctx.Tr.AddNewlyCreatedDBObject(arcLeft, true);
+ }
+ catch { }
+
+ // 右端圆头
+ try
+ {
+ var arcRight = new Arc(new Point3d(ox + L, oy, 0), R, -Math.PI / 2, Math.PI / 2);
+ arcRight.ColorIndex = 7;
+ ctx.Btr.AppendEntity(arcRight);
+ ctx.Tr.AddNewlyCreatedDBObject(arcRight, true);
+ }
+ catch { }
+ }
+
+ #endregion
+
+ #region 方形绘制 - 特征驱动
+
+ private static void DrawBlockFeatures(DrawingContext ctx)
+ {
+ var bag = ctx.Bag;
+
+ // 尝试读取Size1/2/3,如果没有则尝试通用尺寸
+ var size1 = bag.GetDoubleOrNull(KeySize1) ?? bag.GetDoubleOrNull("Size123");
+ var size2 = bag.GetDoubleOrNull(KeySize2);
+ var size3 = bag.GetDoubleOrNull(KeySize3);
+
+ 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;
+ poly.ColorIndex = 7;
+
+ ctx.Btr.AppendEntity(poly);
+ ctx.Tr.AddNewlyCreatedDBObject(poly, true);
+
+ // 宽度标注 (Size1)
+ var tol1Plus = bag.GetDoubleOrNull(KeySize1TolPlus);
+ var tol1Minus = bag.GetDoubleOrNull(KeySize1TolMinus);
+ 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);
+
+ // 高度标注 (Size2)
+ if (size2.HasValue && size2.Value > 0)
+ {
+ var tol2Plus = bag.GetDoubleOrNull(KeySize2TolPlus);
+ var tol2Minus = bag.GetDoubleOrNull(KeySize2TolMinus);
+ 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);
+ }
+
+ // 深度标注 (Size3) - 如果有的话,以文字形式标注
+ if (depth > 0)
+ {
+ var tol3Plus = bag.GetDoubleOrNull(KeySize3TolPlus);
+ var tol3Minus = bag.GetDoubleOrNull(KeySize3TolMinus);
+ 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;
+ text.ColorIndex = 7;
+ ctx.Btr.AppendEntity(text);
+ ctx.Tr.AddNewlyCreatedDBObject(text, true);
+ }
+ catch { }
+ }
+
+ // 未注圆角
+ var unspecifiedFillet = bag.GetDoubleOrNull(KeyUnspecifiedFilletRadiusMax);
+ 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;
+ text.ColorIndex = 7;
+ ctx.Btr.AppendEntity(text);
+ ctx.Tr.AddNewlyCreatedDBObject(text, true);
+ }
+ catch { }
+ }
+
+ // 圆头处圆角
+ if (ctx.HasRoundHead)
+ {
+ var roundHeadFillet = bag.GetDoubleOrNull(KeyRoundHeadFilletRadiusMax);
+ if (roundHeadFillet.HasValue && roundHeadFillet.Value > 0)
+ {
+ // 绘制四角圆角
+ DrawFilletArc(ctx, ox + roundHeadFillet.Value, oy + roundHeadFillet.Value, roundHeadFillet.Value, Math.PI, Math.PI * 1.5);
+ DrawFilletArc(ctx, ox + width - roundHeadFillet.Value, oy + roundHeadFillet.Value, roundHeadFillet.Value, Math.PI * 1.5, Math.PI * 2);
+ DrawFilletArc(ctx, ox + width - roundHeadFillet.Value, oy + height - roundHeadFillet.Value, roundHeadFillet.Value, 0, Math.PI / 2);
+ DrawFilletArc(ctx, ox + roundHeadFillet.Value, oy + height - roundHeadFillet.Value, roundHeadFillet.Value, Math.PI / 2, Math.PI);
+ }
+ }
+ }
+
+ #endregion
+
+ #region 辅助方法
+
+ private static string BuildDimensionText(string baseText, double? tolPlus, double? tolMinus)
+ {
+ if (!tolPlus.HasValue && !tolMinus.HasValue)
+ {
+ return baseText;
+ }
+
+ var result = baseText;
+
+ // 公差格式: 基本尺寸 +上差/-下差
+ if (tolPlus.HasValue && tolMinus.HasValue)
+ {
+ var plusStr = tolPlus.Value >= 0 ? $"+{tolPlus.Value}" : $"{tolPlus.Value}";
+ var minusStr = tolMinus.Value >= 0 ? $"+{tolMinus.Value}" : $"{tolMinus.Value}";
+ result += $"({plusStr}/{minusStr})";
+ }
+ else if (tolPlus.HasValue)
+ {
+ var plusStr = tolPlus.Value >= 0 ? $"+{tolPlus.Value}" : $"{tolPlus.Value}";
+ result += $"({plusStr})";
+ }
+ else if (tolMinus.HasValue)
+ {
+ var minusStr = tolMinus.Value >= 0 ? $"+{tolMinus.Value}" : $"{tolMinus.Value}";
+ result += $"({minusStr})";
+ }
+
+ return result;
+ }
+
+ private static void AddLinearDim(DrawingContext ctx, Point3d pt1, Point3d pt2, Point3d dimLinePt,
+ double rotationDeg, string textOverride)
+ {
+ try
+ {
+ double rotRad = rotationDeg * Math.PI / 180.0;
+ var dim = new RotatedDimension(rotRad, pt1, pt2, dimLinePt, textOverride, ctx.Db.Dimstyle);
+ dim.ColorIndex = 4;
+ ctx.Btr.AppendEntity(dim);
+ ctx.Tr.AddNewlyCreatedDBObject(dim, true);
+ }
+ catch
+ {
+ // 忽略标注错误
+ }
+ }
+
+ 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 { }
+ }
+
+ #endregion
+ }
+}
diff --git a/Cad/HalfSectionDrawer.cs b/Cad/HalfSectionDrawer.cs
index 23abc82..6be4c76 100644
--- a/Cad/HalfSectionDrawer.cs
+++ b/Cad/HalfSectionDrawer.cs
@@ -5,40 +5,39 @@ using CadParamPluging.Common;
namespace CadParamPluging.Cad
{
+ ///
+ /// 半剖视图绘制器 - 已重构为特征驱动模式
+ /// 新的绘制逻辑在 FeatureDrivenDrawer 中实现
+ ///
public static class HalfSectionDrawer
{
- public static void Draw(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature, Point3d? center = null)
+ ///
+ /// 绘制图形(特征驱动模式)
+ ///
+ /// CAD上下文
+ /// 参数包 - 根据存在的参数动态绘制对应特征
+ /// 交付状态(毛料态/车加工态)
+ /// 结构特征(环形/饼盘/轴杆/方体)
+ /// 特殊条件(中心冲孔/有圆头等)
+ /// 绘图中心点
+ public static void Draw(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature,
+ string specialCondition = null, Point3d? center = null)
{
- if (ctx == null)
- {
- throw new ArgumentNullException(nameof(ctx));
- }
-
- if (bag == null)
- {
- throw new ArgumentNullException(nameof(bag));
- }
-
- var drawCenter = center ?? Point3d.Origin;
-
- if (IsRing(structuralFeature))
- {
- DrawRing(ctx, bag, deliveryStatus, drawCenter);
- }
- else if (IsDisk(structuralFeature))
- {
- // 后续实现
- }
- else if (IsShaft(structuralFeature))
- {
- // 后续实现
- }
- else if (IsBlock(structuralFeature))
- {
- // 后续实现
- }
+ // 委托给特征驱动绘图引擎
+ FeatureDrivenDrawer.Draw(ctx, bag, deliveryStatus, structuralFeature, specialCondition, center);
}
+ ///
+ /// 兼容旧接口
+ ///
+ [Obsolete("请使用新的 Draw 方法,支持 specialCondition 参数")]
+ public static void DrawLegacy(CadContext ctx, ParamBag bag, string deliveryStatus, string structuralFeature, Point3d? center = null)
+ {
+ Draw(ctx, bag, deliveryStatus, structuralFeature, null, center);
+ }
+
+ #region 旧代码保留供参考(已废弃)
+
private static bool IsRing(string feature)
{
return !string.IsNullOrWhiteSpace(feature)
@@ -73,7 +72,8 @@ namespace CadParamPluging.Cad
&& deliveryStatus.IndexOf("车加工", StringComparison.OrdinalIgnoreCase) >= 0;
}
- private static void DrawRing(CadContext ctx, ParamBag bag, string deliveryStatus, Point3d center)
+ [Obsolete("已迁移到 FeatureDrivenDrawer")]
+ private static void DrawRingLegacy(CadContext ctx, ParamBag bag, string deliveryStatus, Point3d center)
{
var db = ctx.Database;
var tr = ctx.Transaction;
@@ -242,5 +242,7 @@ namespace CadParamPluging.Cad
// 忽略
}
}
+
+ #endregion
}
}
diff --git a/CadParamPluging.csproj b/CadParamPluging.csproj
index 5c9b1e5..5445828 100644
--- a/CadParamPluging.csproj
+++ b/CadParamPluging.csproj
@@ -77,6 +77,7 @@
+
diff --git a/Common/ParamBag.cs b/Common/ParamBag.cs
index 68e3250..01fcc4e 100644
--- a/Common/ParamBag.cs
+++ b/Common/ParamBag.cs
@@ -115,6 +115,54 @@ namespace CadParamPluging.Common
return fallback;
}
+
+ ///
+ /// 检查参数是否存在且有有效值
+ ///
+ public bool Has(string key)
+ {
+ var s = GetString(key);
+ return !string.IsNullOrWhiteSpace(s);
+ }
+
+ ///
+ /// 获取可空的 double 值,参数不存在或无效时返回 null
+ ///
+ public double? GetDoubleOrNull(string key)
+ {
+ var s = GetString(key);
+ if (string.IsNullOrWhiteSpace(s))
+ {
+ return null;
+ }
+
+ if (double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var v))
+ {
+ return v;
+ }
+
+ if (double.TryParse(s, NumberStyles.Float, CultureInfo.CurrentCulture, out v))
+ {
+ return v;
+ }
+
+ return null;
+ }
+
+ ///
+ /// 获取所有已设置的参数Key列表
+ ///
+ public IEnumerable GetKeys()
+ {
+ if (Items == null)
+ {
+ return Enumerable.Empty();
+ }
+
+ return Items
+ .Where(x => x != null && !string.IsNullOrWhiteSpace(x.Key))
+ .Select(x => x.Key);
+ }
}
[Serializable]
diff --git a/UI/ParamDrawingPanel.cs b/UI/ParamDrawingPanel.cs
index b813172..72f13c7 100644
--- a/UI/ParamDrawingPanel.cs
+++ b/UI/ParamDrawingPanel.cs
@@ -617,11 +617,13 @@ namespace CadParamPluging.UI
AppendLog($"已删除原有图形: 红色外框={removeResult.OuterFrameErased}, CAXA图层={removeResult.CaxaLayerErased}, 标注={removeResult.DimensionLayerErased}, 保留右上角={removeResult.DimensionLayerKept}");
// 根据模板参数绘制半剖视图,居中放置于原图纸位置
+ // 使用特征驱动模式:根据参数存在性动态绘制对应特征
HalfSectionDrawer.Draw(
ctx,
bag,
tplParams.ProjectType, // 交付状态
tplParams.SheetSize, // 结构特征
+ tplParams.Scale, // 特殊条件(中心冲孔/有圆头等)
removeResult.OriginalCenter // 原图纸中心点
);
@@ -776,19 +778,30 @@ namespace CadParamPluging.UI
try
{
// ==========================================
- // 测试参数构造
+ // 测试参数构造 - 演示特征驱动模式
// ==========================================
var bag = new ParamBag();
// 锻件参数 (调整为细长型,符合参考图比例)
bag.Set("OuterDiameter1", "160");
+ bag.Set("OuterDiameter1TolPlus", "2");
+ bag.Set("OuterDiameter1TolMinus", "-2");
bag.Set("InnerDiameter2", "100");
+ bag.Set("InnerDiameter2TolPlus", "3");
+ bag.Set("InnerDiameter2TolMinus", "-3");
bag.Set("Height1", "400");
+ bag.Set("Height1TolPlus", "5");
+ bag.Set("Height1TolMinus", "-5");
// 零件参数 (车加工)
bag.Set("OuterDiameter1Prime", "150");
bag.Set("InnerDiameter2Prime", "110");
bag.Set("Height1Prime", "380");
+
+ // 可选特征参数 - 根据模板绑定动态显示
+ bag.Set("UnspecifiedFilletRadiusMax", "6"); // 未注圆角半径R≤6
+ bag.Set("MinWallThickness", "25"); // 最小壁厚T
+ // bag.Set("InnerRadiusMax", "8"); // 内径半径R≤8(中心冲孔时用)
var doc = AcadApp.DocumentManager.MdiActiveDocument;
if (doc == null)
@@ -800,12 +813,20 @@ namespace CadParamPluging.UI
using (doc.LockDocument())
using (var ctx = new CadContext(doc))
{
- // 调用统一的绘图逻辑
- HalfSectionDrawer.Draw(ctx, bag, "车加工", "环形");
+ // 调用特征驱动绘图引擎
+ // 会根据bag中存在的参数自动绘制对应特征
+ HalfSectionDrawer.Draw(
+ ctx,
+ bag,
+ "车加工", // 交付状态
+ "环形", // 结构特征
+ null // 特殊条件(可选:中心冲孔/有圆头等)
+ );
ctx.Commit();
- AppendLog($"测试绘制完成 (调用 HalfSectionDrawer)");
+ AppendLog($"测试绘制完成 (特征驱动模式)");
+ AppendLog($"已绘制特征: 外轮廓、内孔(虚线)、公差标注、未注圆角、壁厚、零件(填充)");
}
// Zoom Extents