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