CadParamPluging/Cad/BlockRawFreeForgeNoRoundHeadDrawer.cs

868 lines
42 KiB
C#
Raw Permalink 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 Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using CadParamPluging.Common;
namespace CadParamPluging.Cad
{
public static class BlockRawFreeForgeNoRoundHeadDrawer
{
public static void Draw(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 FeatureDrivenDrawer.DrawingContext
{
Ctx = ctx,
Bag = effectiveBag,
OriginalBag = bag,
Center = center,
DeliveryStatus = "毛料态",
StructuralFeature = "方体",
SpecialCondition = "无圆头", // Updated
ProcessMethod = "自由锻",
Btr = btr,
Style = new DrawingStyleManager(db, tr)
};
DrawCore(context);
}
// COPIED FROM FeatureDrivenDrawer.DrawBlockRawFreeForgeRoundHeadCore AND MODIFIED
private static void DrawCore(FeatureDrivenDrawer.DrawingContext ctx)
{
var bag = ctx.Bag;
// BoxSize1 (Width - 总长度)
// BoxSize2 (Height - 高度/直径)
var width = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1);
var height = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2);
if (!width.HasValue || width.Value <= 0 || !height.HasValue || height.Value <= 0)
{
return;
}
double H = height.Value;
double W = width.Value;
// 外轮廓圆弧:高度固定为 H/5 -> 原"有圆头"逻辑
// 依据用户新需求 (2026-01-29): 黄色部分(圆弧高)是粉色部分(总高H)的1/5
// double arcHeight = H / 5.0; // OLD Round Head Logic
// 【关键修改点】无圆头模式圆弧高度为0
double arcHeight = 0;
// 视觉比例:总宽度映射
double visualTotalW = H * 3.0; // 保持视觉宽高比约 3:1
// 绘图坐标计算 (保持居中)
double ox = ctx.Center.X - visualTotalW / 2.0; // 左边界 (圆弧最左端)
double oy = ctx.Center.Y - H / 2.0; // 底边
// 内框边界(从左圆弧弦线到右圆弧弦线)
// 注意ox 是圆弧顶点xInnerLeft 应该是弦线位置
double xInnerLeft = ox + arcHeight;
double xInnerRight = ox + visualTotalW - arcHeight;
double innerWidth = xInnerRight - xInnerLeft;
// 获取内框圆角参数:优先取 UnspecifiedFilletRadiusMax如果没有则取 BoxFilletRadiusMax
var innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyUnspecifiedFilletRadiusMax);
if (!innerFilletParam.HasValue || innerFilletParam.Value <= 0)
{
innerFilletParam = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxFilletRadiusMax);
}
double innerFilletR = innerFilletParam.HasValue && innerFilletParam.Value > 0
? innerFilletParam.Value
: 0;
// 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧)
// 修正:圆弧弦线需向内缩进 innerFilletR以与锻件圆角起点对齐
// 【关键修改点】无圆头模式:不需要绘制大圆弧外轮廓,或者绘制一个直角的轮廓。
// 由于 DrawBoxSideFrame 已经绘制了左右两侧的边框线(含圆角),
// DrawBoxSectionContourWithFillet 绘制了中间。
// 它们的合集就是外轮廓。
// 原代码 DrawBlockRoundHeadOutline 是为了画那个额外的“耳朵”圆弧。
// 这里我们只需要一个简单的矩形外框作为“外轮廓实体”(如果需要)
// 或者就像原逻辑一样,如果没有大圆弧,就不画 DrawBlockRoundHeadOutline。
// 但用户要求“包含尺寸标注和参数标注线都复制过来”,且标注依赖于这些几何位置。
// 这里我们跳过 DrawBlockRoundHeadOutline因为那是特定的圆头形状。
// 如果用户想要最外侧也是封闭的线SideFrames + SectionContour 其实不完全封闭(中间有重叠线)。
// 让我们看看原逻辑DrawBlockRoundHeadOutline 是独立的一圈粗线。
// 此处我们是否需要一个“无圆头”的外圈粗线?
// 我们可以仅依靠内部组件,或者画一个大的圆角矩形包围所有。
// 鉴于 SideFrame 已经画了外侧线,我们这里可能不需要额外的 Outline
// UNLESS the user implies "No Round Head" means "Rectangular Head".
// Let's assume the Side Frames define the shape.
// DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, innerFilletR); // SKIPPED
// 2. 绘制分段式内框(左、中、右三段)以支持中间剖面区域的独立圆角
// 中间区域(剖面)作为主体,如果设置了圆角,四个角都应为圆角
// 计算视觉上的剖面宽度
// 依据用户需求尺寸3 (BoxSize3) 对应剖面的宽
var size3 = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3);
double visualSectionWidth;
if (size3.HasValue && size3.Value > 0)
{
// 按比例映射VisualW / PhysicalW
double scale = visualTotalW / W;
visualSectionWidth = size3.Value * scale;
if (visualSectionWidth > innerWidth * 0.95) visualSectionWidth = innerWidth * 0.95;
}
else
{
visualSectionWidth = innerWidth / 3.0;
}
// 居中布置剖面
double sectionOffset = (innerWidth - visualSectionWidth) / 2.0;
double xSectionLeft = xInnerLeft + sectionOffset;
double xSectionRight = xSectionLeft + visualSectionWidth;
// 绘制中间剖面框(带圆角 - 根据需求保留)
DrawBoxSectionContourWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR);
// 绘制左侧框线(连接左端圆弧和中间剖面框)
// 用户需求:
// 1. 去掉最外侧的圆角 -> filletR 传入 innerFilletR (Updated per requirement)
// 2. 横向闭合:水平线需延伸到中间框圆角结束的地方 (xSectionLeft + innerFilletR) 以实现无缝连接
// 3. 竖向不闭合DrawBoxSideFrame 内部不 Closed且不画连接处的竖线 (Now Closed=false in Helper)
DrawBoxSideFrame(ctx, xInnerLeft, xSectionLeft + innerFilletR, oy, H, innerFilletR, isLeft: true);
// 绘制右侧框线
// 类似的,水平线起点延伸到 (xSectionRight - innerFilletR)
DrawBoxSideFrame(ctx, xSectionRight - innerFilletR, xInnerRight, oy, H, innerFilletR, isLeft: false);
// 3. 剖面填充 - 只在中间区域
if (innerFilletR > 0.01)
{
DrawRingSectionHatchWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, innerFilletR);
}
else
{
DrawBlockSectionHatch(ctx, xSectionLeft, xSectionRight, oy, H);
}
// 5. 零件轮廓框(黄色双点划线矩形 - 在内框内,与锻件有一定间距)
var widthPrime = bag.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1Prime);
var heightPrime = bag.GetDoubleOrNull(FeatureDrivenDrawer.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(FeatureDrivenDrawer.KeyBoxSize1) ?? W;
var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1);
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize1TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方,用括号包裹
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize1Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})";
}
// 标注点从内框左右边界开始(连接到矩形内框) - Modify Y to center to ensure extension lines touch the geometry
double dimOriginY = oy + innerFilletR;
AddLinearDim(
ctx,
new Point3d(xInnerLeft, dimOriginY, 0),
new Point3d(xInnerRight, dimOriginY, 0),
new Point3d((xInnerLeft + xInnerRight) / 2, oy - 25, 0),
0,
dimText);
}
// 7. 高度标注 (BoxSize2) - 位于右侧
// 标注从锻件顶部到底部
{
var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2) ?? H;
var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2);
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize2TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize2Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})";
}
// 标注从圆弧最外侧开始(避免与圆弧轮廓线重叠)
double xOuterRight = ox + visualTotalW;
// 修正:由于去掉了最外侧圆角,标注界线起点直接对齐最外侧边界
// Refinment: 减去倒角半径,使尺寸界线能够接触到直边,避免因为圆角产生悬空
double xDimOrigin = xOuterRight - 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. 中间矩形宽度标注 (BoxSize3) - 位于上方
// 对应剖面区域宽度
{
var val = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3) ?? 0;
var str = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3);
if (val > 0)
{
var tolPlus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3TolPlus);
var tolMinus = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3TolMinus);
var tolPlusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3TolPlus);
var tolMinusStr = ctx.OriginalBag?.GetString(FeatureDrivenDrawer.KeyBoxSize3TolMinus);
var dimText = BuildDimensionText(FormatDimNumber(val, str), tolPlus, tolMinus, tolPlusStr, tolMinusStr);
// 添加零件尺寸 (Prime) 到下方
var valPrime = ctx.OriginalBag?.GetDoubleOrNull(FeatureDrivenDrawer.KeyBoxSize3Prime);
if (valPrime.HasValue && valPrime.Value > 0)
{
dimText += $"\\X({FormatDimNumber(valPrime.Value, null)})";
}
// 修正:引线向下延伸 innerFilletR以接触直边
double dimYStart = oy + H - innerFilletR;
AddLinearDim(
ctx,
new Point3d(xSectionLeft, dimYStart, 0),
new Point3d(xSectionRight, dimYStart, 0),
new Point3d((xSectionLeft + xSectionRight) / 2, oy + H + 15, 0),
0,
dimText);
}
}
// 9. 硬度符号 - 从剖面右侧引出
var hardnessVal = bag.GetString(FeatureDrivenDrawer.KeyHardness);
if (!string.IsNullOrWhiteSpace(hardnessVal) && hardnessVal != "空")
{
double xStart = xSectionRight;
double yStart = oy + H * 0.25;
FeatureDrivenDrawer.DrawHardnessSymbol(ctx, xStart, yStart, hardnessVal);
}
// 10. 标刻内容引线 - 从剖面左侧引出
var markingText = bag.GetString(FeatureDrivenDrawer.KeyMarkingContent);
if (!string.IsNullOrWhiteSpace(markingText))
{
double xTarget = xSectionLeft;
double yTarget = oy + H * 0.75;
DrawSpecialHBLeaderToTop(ctx, xTarget, yTarget, markingText);
}
}
// =============== HELPERS COPIED FROM FeatureDrivenDrawer AND ADAPTED ===============
private static void DrawBlockPartContour(FeatureDrivenDrawer.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 {}
}
private static void DrawBoxSectionContourWithFillet(FeatureDrivenDrawer.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);
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);
}
private static void DrawBoxSideFrame(FeatureDrivenDrawer.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);
var bulge = -Math.Tan(Math.PI / 8.0);
var poly = new Polyline();
// 为了闭合缝隙处理:
// 中间剖面框 (DrawBoxSectionContourWithFillet) 绘制在 xSectionLeft ~ xSectionRight
// 左侧框线 DrawBoxSideFrame(..., xInnerLeft, xSectionLeft, ...) 是左边的部分
// 右侧框线 DrawBoxSideFrame(..., xSectionRight, xInnerRight, ...) 是右边的部分
//
// 问题在于:当 SideFrame 的 filletR=0 时,它画的是纯直线矩形。
// 而中间的 DrawBoxSectionContourWithFillet 依然可能有 innerFilletR > 0导致它的角是圆的。
// 当两个矩形拼接时如果中间那个有圆角而旁边的是直角交界处的高度看起来是一样的yBottom, yTop理论上应该重合。
// 但如果中间框的圆角导致其垂直边缩短圆角起止点变化而SideFrame只是简单的画到 yTop/yBottom
// 它们在垂直方向上是重合的。
//
// 观察截图,缝隙似乎出现在连接处。
// 中间框 DrawBoxSectionContourWithFillet 在 xLeft, xRight 处的垂直线段是:
// 左侧: (xLeft, yBottom+r) 到 (xLeft, yTop-r)
// 右侧: (xRight, yBottom+r) 到 (xRight, yTop-r)
//
// SideFrame (以左侧为例, isLeft=true, filletR=0):
// xEnd 是连接点 (即 xLeft)。
// 它画的线包含 (xEnd, yTop) -> (xEnd, yBottom)
//
// 理论上 (xEnd, yBottom) 和 (xSectionLeft, yBottom+r) 之间有高度差 r但这部分重叠区域应该由圆弧填补
// 不DrawBoxSectionContourWithFillet 画了完整的闭合回路,包括圆角。
// SideFrame 画了另一侧。
// 只要 SideFrame 的垂直边完整覆盖 (yBottom 到 yTop),就不应该有空隙,除非 SideFrame 的 x 坐标没对上。
//
// 另一种可能是DrawBoxSectionContourWithFillet 的圆角不仅影响y也影响x圆角是在角内切的。
//
// 让我们仔细看 DrawBoxSectionContourWithFillet 的左边线:
// Vertex 6: (xLeft, yTop - r)
// Vertex 7: (xLeft, yBottom + r)
// 这两点之间是直线。
// 下方圆角从 (xLeft, yBottom+r) 到 (xLeft+r, yBottom)。
// 这意味着在 xLeft 这一条垂直线上,只有 yBottom+r 到 yTop-r 这一段是有线的。
// yBottom 到 yBottom+r 这一段是空的(因为线往右弯去画圆角了)。
//
// 而 SideFrame (isLeft=true, filletR=0) 在 xEnd (即 xLeft) 处的线是:
// Vertex 0: (xEnd, yBottom)
// Vertex ... (最终回到 xEnd, yTop)
// 如果 SideFrame 是开放的 (Closed=false),我们来看看它的路径。
// isLeft=true:
// P0: (xEnd, yBottom)
// ... 画左边的框 ...
// P4: (xEnd, yTop)
// 它画了两条水平线回到 center但是没有画连接这两个点的垂直线
// 原逻辑可能是依赖中间框来画这条线?
// 是的FeatureDrivenDrawer 里的逻辑通常是一块块拼接,共用边如果不画两遍的话。
//
// 修正方案:
// 无论中间框是否有圆角SideFrame 作为“框线”,应该把与中间框接触的那条边补上,或者延伸进去。
// 既然中间框是有圆角的,那么在角的地方,中间框的线是缩进去的。
// SideFrame 必须画出完整的一条垂直线 (xEnd, yBottom) 到 (xEnd, yTop) 来“封口”,
// 虽然这会导致与中间框的直线部分重叠,但能盖住圆角留下的空隙。
//
// 当前代码 isLeft=true 时:
// poly.AddVertexAt(0, new Point2d(xEnd, yBottom), 0, 0, 0);
// ... (往左画) ...
// poly.AddVertexAt(5, new Point2d(xEnd, yTop), 0, 0, 0);
// 确实是开口的“C”形。
//
// 我们需要让它闭合,或者至少画上那条连接线。
// 直接将 poly.Closed 设为 true 即可!
// 这样 (xEnd, yTop) 会自动连回 (xEnd, yBottom),形成一个完整的矩形框。
// 这样右边那条垂直线就会被画出来,填补中间框圆角留下的上下空缺。
if (isLeft)
{
// 左侧框:起点在右下 (与中间框接壤处),顺时针绘制
// P0: (xEnd, yBottom)
poly.AddVertexAt(0, new Point2d(xEnd, yBottom), 0, 0, 0);
if (r > 0.01)
{
poly.AddVertexAt(1, new Point2d(xStart + r, yBottom), bulge, 0, 0);
poly.AddVertexAt(2, new Point2d(xStart, yBottom + r), 0, 0, 0);
poly.AddVertexAt(3, new Point2d(xStart, yTop - r), bulge, 0, 0);
poly.AddVertexAt(4, new Point2d(xStart + r, yTop), 0, 0, 0);
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);
}
}
else
{
// 右侧框:起点在左上 (与中间框接壤处),顺时针绘制
// P0: (xStart, yTop)
poly.AddVertexAt(0, new Point2d(xStart, yTop), 0, 0, 0);
if (r > 0.01)
{
poly.AddVertexAt(1, new Point2d(xEnd - r, yTop), bulge, 0, 0);
poly.AddVertexAt(2, new Point2d(xEnd, yTop - r), 0, 0, 0);
poly.AddVertexAt(3, new Point2d(xEnd, yBottom + r), bulge, 0, 0);
poly.AddVertexAt(4, new Point2d(xEnd - r, yBottom), 0, 0, 0);
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);
}
private static void DrawRingSectionHatchWithFillet(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height, double filletR)
{
try
{
var yTop = yBottom + height;
var width = xRight - xLeft;
var maxR = Math.Min(width * 0.5, height * 0.5);
var r = Math.Min(filletR, maxR);
var bulge = Math.Tan(Math.PI / 8.0);
var boundary = new Polyline();
if (r > 0.01)
{
boundary.AddVertexAt(0, new Point2d(xLeft + r, yBottom), 0, 0, 0);
boundary.AddVertexAt(1, new Point2d(xRight - r, yBottom), bulge, 0, 0);
boundary.AddVertexAt(2, new Point2d(xRight, yBottom + r), 0, 0, 0);
boundary.AddVertexAt(3, new Point2d(xRight, yTop - r), bulge, 0, 0);
boundary.AddVertexAt(4, new Point2d(xRight - r, yTop), 0, 0, 0);
boundary.AddVertexAt(5, new Point2d(xLeft + r, yTop), bulge, 0, 0);
boundary.AddVertexAt(6, new Point2d(xLeft, yTop - r), 0, 0, 0);
boundary.AddVertexAt(7, new Point2d(xLeft, yBottom + r), bulge, 0, 0);
}
else
{
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, yTop), 0, 0, 0);
boundary.AddVertexAt(3, new Point2d(xLeft, yTop), 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);
boundary.Erase(true);
}
catch {}
}
private static void DrawBlockSectionHatch(FeatureDrivenDrawer.DrawingContext ctx, double xLeft, double xRight, double yBottom, double height)
{
DrawRingSectionHatchWithFillet(ctx, xLeft, xRight, yBottom, height, 0);
}
private static ParamBag ScaleParamBag(ParamBag original, double scaleFactor)
{
if (original == null) return null;
if (Math.Abs(scaleFactor - 1.0) < 0.000001) return original;
var newBag = new ParamBag();
var scaleKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
FeatureDrivenDrawer.KeyBoxSize1,
FeatureDrivenDrawer.KeyBoxSize2,
FeatureDrivenDrawer.KeyBoxSize3,
FeatureDrivenDrawer.KeyBoxSize1TolPlus,
FeatureDrivenDrawer.KeyBoxSize1TolMinus,
FeatureDrivenDrawer.KeyBoxSize2TolPlus,
FeatureDrivenDrawer.KeyBoxSize2TolMinus,
FeatureDrivenDrawer.KeyBoxSize3TolPlus,
FeatureDrivenDrawer.KeyBoxSize3TolMinus,
FeatureDrivenDrawer.KeyBoxFilletRadiusMax,
FeatureDrivenDrawer.KeyUnspecifiedFilletRadiusMax,
FeatureDrivenDrawer.KeyBoxSize1Prime,
FeatureDrivenDrawer.KeyBoxSize2Prime,
FeatureDrivenDrawer.KeyBoxSize3Prime
};
foreach (var key in original.GetKeys())
{
var strVal = original.GetString(key);
if (scaleKeys.Contains(key) && double.TryParse(strVal, out var val))
{
newBag.Set(key, (val * scaleFactor).ToString("0.###"));
}
else
{
newBag.Set(key, strVal);
}
}
return newBag;
}
private static void AddLinearDim(FeatureDrivenDrawer.DrawingContext ctx, Point3d p1, Point3d p2, Point3d ptDim, double rot, string text)
{
try
{
var dim = new RotatedDimension();
dim.SetDatabaseDefaults();
dim.XLine1Point = p1;
dim.XLine2Point = p2;
dim.DimLinePoint = ptDim;
dim.Rotation = rot * Math.PI / 180.0;
dim.DimensionStyle = ctx.Db.Dimstyle;
dim.DimensionText = text;
// Color Index 3 = Green
dim.ColorIndex = 3;
// --- 强制应用覆盖属性以确保显示效果 ---
// 颜色覆盖:尺寸线、尺寸界线、文字全部强制绿色
// Dimclrd: Dimension line color
// Dimclre: Extension line color
// Dimclrt: Text color
// 3 = Green
SetDimColor(dim, "Dimclrd", 3);
SetDimColor(dim, "Dimclre", 3);
SetDimColor(dim, "Dimclrt", 3);
// 强制绘制尺寸线即使文字在界线外 (Dimtofl = true)
SetDimBool(dim, "Dimtofl", true);
// 文字/箭头最佳调整 (Dimatfit = 3: Best fit)
SetDimInt(dim, "Dimatfit", 3);
// 文字垂直位置 (Dimtad = 1: Above dimension line)
SetDimInt(dim, "Dimtad", 1);
// 文字水平对齐 (Dimtih = false: Aligned with dimension line, not horizontal)
SetDimBool(dim, "Dimtih", false);
SetDimBool(dim, "Dimtoh", false);
// 尺寸参数微调
SetDimDouble(dim, "Dimtxt", 3.5); // 文字高度
SetDimDouble(dim, "Dimasz", 2.5); // 箭头大小
SetDimDouble(dim, "Dimgap", 1.0); // 文字偏移
SetDimDouble(dim, "Dimexe", 1.0); // 延伸超出量
SetDimDouble(dim, "Dimexo", 0.0); // 原点偏移
ctx.Style?.Apply(dim, DrawingStyleManager.Role.Dimension);
// 再次确保 Entity 颜色为绿色
dim.ColorIndex = 3;
ctx.Btr.AppendEntity(dim);
ctx.Tr.AddNewlyCreatedDBObject(dim, true);
// 尝试重计算以刷新显示块
try { dim.RecomputeDimensionBlock(true); } catch {}
}
catch {}
}
// 辅助反射设置方法 (Copy from FeatureDrivenDrawer to avoid dependency issues or access restrictions)
private static void SetDimColor(Dimension dim, string prop, int colorIndex)
{
try
{
// Property type for color override is usually Autodesk.AutoCAD.Colors.Color
// But specifically for Dimclrd/e/t properties on the object wrapper, they might be exposed as Color objects.
// However, standard COM/ActiveX way involves variables. In .NET API, we usually set Dim Variables on the Database or style.
// BUT, effectively, we can set overrides on the Dimension entity itself if it supports the property via reflection
// typically mirroring the system variable names.
// Let's rely on the property reflection if available, but for color, it's tricky.
// The most robust way using .NET API for dimension overrides is strictly through the properties if exposed,
// OR modifying the XData/Dxf codes.
//
// WAIT. FeatureDrivenDrawer uses TrySetDimProp with reflection. Let's replicate that simple logic.
// Note: Dimension class properties in .NET don't map 1:1 to DIM vars (e.g. Dimclrd is not a property of Dimension).
// They are properties of DimensionStyle. To override on an instance, we strictly need to simple properties if they exist?
// Actually, the previous code used reflection on 'dim' object.
// Let's double check if "Dimclrd" exists on RotatedDimension. It usually doesn't.
// It holds "DimColor" maybe? No.
//
// Re-reading FeatureDrivenDrawer.TrySetDimProp ...
// It sets properties via reflection.
// Let's implement a safer version: using dynamic if possible, or just standard properties.
// Actually, correct way to override dim var on an entity is NOT via properties (they don't exist),
// but by creating a DimStyle override or setting specific properties like DimensionText.
// HOWEVER, RotatedDimension DOES have properties like 'Dimclrd' EXPOSED dynamically in some contexts?
// No, standard .NET API does not expose 'Dimclrd' as a property on RotatedDimension.
//
// Wait, FeatureDrivenDrawer code shown in Step 182 DOES NOT show TrySetDimProp setting 'Dimclrd'.
// Step 200 shows TryApplyDimSizeOverrides calling TrySetDimProp(dim, "Dimclrd", 3).
// This implies that at runtime, the Dimension object (or a wrapper) HAS these properties?
// OR, the User's environment has an extension method?
// OR, FeatureDrivenDrawer.TrySetDimProp handles it magic?
// Let's look at FeatureDrivenDrawer.TrySetDimProp in Step 182.
// It does `obj.GetType().GetProperty(propName)`.
// If this works, then RotatedDimension must have these properties.
// Let's assume they exist as per the working example in FeatureDrivenDrawer.
var p = dim.GetType().GetProperty(prop);
if (p != null && p.CanWrite)
{
if (p.PropertyType == typeof(Autodesk.AutoCAD.Colors.Color))
{
p.SetValue(dim, Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByAci, (short)colorIndex), null);
}
else if (p.PropertyType == typeof(int))
{
p.SetValue(dim, colorIndex, null);
}
}
}
catch {}
}
private static void SetDimBool(Dimension dim, string prop, bool val)
{
try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {}
}
private static void SetDimDouble(Dimension dim, string prop, double val)
{
try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {}
}
private static void SetDimInt(Dimension dim, string prop, int val)
{
try { dim.GetType().GetProperty(prop)?.SetValue(dim, val, null); } catch {}
}
private static string FormatDimNumber(double value, string rawInput = null)
{
try
{
// If rawInput is provided and matches the value, use its precision
if (!string.IsNullOrWhiteSpace(rawInput) && double.TryParse(rawInput, out var d) && Math.Abs(d - value) < 0.000001)
{
int decIndex = rawInput.IndexOf('.');
int decimals = 0;
if (decIndex >= 0)
{
decimals = rawInput.Length - decIndex - 1;
}
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
}
int autoDecimals = str.Length - decimalIndex - 1;
if (autoDecimals == 0) return str;
string format = "0." + new string('0', autoDecimals);
return value.ToString(format);
}
catch
{
return value.ToString();
}
}
private static string BuildDimensionText(string baseText, double? tolPlus, double? tolMinus, string tolPlusStr = null, string tolMinusStr = null)
{
if (!tolPlus.HasValue && !tolMinus.HasValue)
{
return baseText;
}
bool isSymmetrical = false;
if (tolPlus.HasValue && tolMinus.HasValue)
{
if (Math.Abs(Math.Abs(tolPlus.Value) - Math.Abs(tolMinus.Value)) < 0.000001)
{
isSymmetrical = true;
}
}
if (isSymmetrical)
{
var val = Math.Abs(tolPlus.Value);
return $"{baseText}%%p{FormatDimNumber(val, tolPlusStr)}";
}
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)
{
double vm = tolMinus.Value;
string formattedVal = FormatDimNumber(Math.Abs(vm), tolMinusStr);
if (Math.Abs(vm) < 0.000001)
{
minusStr = formattedVal;
}
else
{
minusStr = $"-{formattedVal}";
}
}
if (tolPlus.HasValue && tolMinus.HasValue)
{
return $"{baseText} {{\\H{tolScale}x;\\S{plusStr}^{minusStr};}}";
}
var single = tolPlus.HasValue ? plusStr : minusStr;
return $"{baseText} {{\\H{tolScale}x;{single}}}";
}
private static void DrawSpecialHBLeaderToTop(FeatureDrivenDrawer.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);
double textLen = (string.IsNullOrEmpty(textContent) ? 10 : textContent.Length) * 2.5;
if (textLen < 20) textLen = 20;
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 {}
}
}
}