feat: 新增 BlockRawFreeForgeRoundHeadDrawer 类,实现自由锻圆头毛坯的参数化绘图功能。

This commit is contained in:
sladro 2026-02-28 11:07:21 +08:00
parent ad9968f622
commit ad76f1bb32

View File

@ -135,34 +135,40 @@ namespace CadParamPluging.Cad
// 使用 sectionFilletR
DrawBoxSectionContourWithFillet(ctx, xSectionLeft, xSectionRight, oy, H, sectionFilletR);
// Calculate effective head fillet R (clamped by side frame width) to ensure Outline matches SideFrame
// Calculate effective head fillet R (clamped by side frame width)
double sideFrameWidth = xSectionLeft - xInnerLeft;
double maxR = Math.Min(sideFrameWidth * 0.5, H * 0.5);
double effectiveHeadR = Math.Min(headFilletR, maxR);
// 绘制左侧框线(连接左端圆弧和中间剖面框)
// 左端: xInnerLeft (需处理左上下角的圆角 - 使用 headFilletR)
// 右端: xSectionLeft (连接到中间框的直边)
// Note: Extend horizontally by sectionFilletR to achieve "Horizontal Closure" with the section fillet
DrawBoxSideFrame(ctx, xInnerLeft, xSectionLeft + sectionFilletR, oy, H, headFilletR, isLeft: true);
double Rm = (H * H) / (8.0 * arcHeight) + arcHeight / 2.0;
double D_tangent = (Rm >= effectiveHeadR) ? Math.Sqrt(Math.Max(0, (Rm - effectiveHeadR) * (Rm - effectiveHeadR) - (H / 2.0 - effectiveHeadR) * (H / 2.0 - effectiveHeadR))) : (Rm - arcHeight);
double xf_left = effectiveHeadR > 0 ? (xInnerLeft + Rm - arcHeight) - D_tangent : xInnerLeft;
double xf_right = effectiveHeadR > 0 ? (xInnerRight - Rm + arcHeight) + D_tangent : xInnerRight;
// 绘制右侧框线
// 左端: xSectionRight (连接到中间框的直边)
// 右端: xInnerRight (需处理右上下角的圆角 - 使用 headFilletR)
// Note: Extend horizontally inwards by sectionFilletR
DrawBoxSideFrame(ctx, xSectionRight - sectionFilletR, xInnerRight, oy, H, headFilletR, isLeft: false);
double xc_left = xInnerLeft + Rm - arcHeight;
double fx_L_tang = effectiveHeadR > 0 ? xc_left + (Rm / (Rm - effectiveHeadR)) * (xf_left - xc_left) : xInnerLeft;
double fy_L_tangBot = effectiveHeadR > 0 ? (oy + H / 2.0) + (Rm / (Rm - effectiveHeadR)) * ((oy + effectiveHeadR) - (oy + H / 2.0)) : oy;
// 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧)
// 修正:圆弧弦线需向内缩进 effectiveHeadR (Clamped),以与锻件外侧圆角起点对齐
// This ensures the Big Arc starts curving exactly where the Side Frame vertical edge ends.
DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, effectiveHeadR);
double xc_right = xInnerRight - Rm + arcHeight;
double fx_R_tang = effectiveHeadR > 0 ? xc_right + (Rm / (Rm - effectiveHeadR)) * (xf_right - xc_right) : xInnerRight;
double fy_R_tangBot = effectiveHeadR > 0 ? (oy + H / 2.0) + (Rm / (Rm - effectiveHeadR)) * ((oy + effectiveHeadR) - (oy + H / 2.0)) : oy;
// 绘制左侧框线(只画横向连接线)
DrawBoxSideFrame(ctx, xf_left, xSectionLeft + sectionFilletR, oy, H, 0, isLeft: true);
// 绘制右侧框线(只画横向连接线)
DrawBoxSideFrame(ctx, xSectionRight - sectionFilletR, xf_right, oy, H, 0, isLeft: false);
// 1. 绘制完整锻件外轮廓(方体有圆头 - 白色,大圆弧及外侧平滑过渡圆角)
DrawBlockRoundHeadOutline(ctx, ox, oy, visualTotalW, H, arcHeight, effectiveHeadR, xf_left, xf_right);
// 绘制圆角的斜向标注如果存在圆角4-R<=*
if (effectiveHeadR > 0)
{
// 左上角
double yTop = oy + H;
double cxLeft = xInnerLeft + effectiveHeadR;
double cxLeft = xf_left;
double cyLeft = yTop - effectiveHeadR;
DrawHeadFilletRadiusLeader(ctx, cxLeft, cyLeft, effectiveHeadR, true);
@ -228,13 +234,12 @@ namespace CadParamPluging.Cad
// 修正:标注需涵盖内框宽度 (矩形部分)
// 修正:引线起点改为内框边界 (xInnerLeft/Right) 和高度底部上移圆角半径,确保闭合到白色线框
// 使用 headFilletR 作为 Offset因为这是侧边框的倒角
double dimOriginY = oy + headFilletR;
// 修正:引线起点严格匹配倒角与圆弧相切处的极限点坐标 (fx_L_tang, fy_L_tangBot)
AddLinearDim(
ctx,
new Point3d(xInnerLeft, dimOriginY, 0),
new Point3d(xInnerRight, dimOriginY, 0),
new Point3d((xInnerLeft + xInnerRight) / 2, oy - 25, 0),
new Point3d(fx_L_tang, fy_L_tangBot, 0),
new Point3d(fx_R_tang, fy_R_tangBot, 0),
new Point3d((fx_L_tang + fx_R_tang) / 2, oy - 25, 0),
0,
dimText);
}
@ -327,55 +332,146 @@ namespace CadParamPluging.Cad
}
}
private static void DrawBlockRoundHeadOutline(FeatureDrivenDrawer.DrawingContext ctx, double ox, double oy, double totalWidth, double height, double arcHeight, double innerFilletR)
private static void DrawBlockRoundHeadOutline(FeatureDrivenDrawer.DrawingContext ctx, double ox, double oy, double totalWidth, double height, double arcHeight, double innerFilletR, double xf_left, double xf_right)
{
try
{
double H = height;
double r = innerFilletR;
// Calculate geometric bounds
double xInnerLeft = ox + arcHeight;
double xInnerRight = ox + totalWidth - arcHeight;
// 修正:为了确保视觉上与圆角完全闭合,稍微向圆角区域延伸一点 (0.8 * r)
// 用户的需求是“再高一点和低一点”,这意味着需要一定的重叠量来消除缝隙
double overlapFactor = 0.8;
double yBottomStraight = oy + r * overlapFactor;
double yTopStraight = oy + H - r * overlapFactor;
double chordLength = yTopStraight - yBottomStraight;
if (chordLength < 0.1) return; // Should not happen for reasonable params
double w = arcHeight;
if (w < 0.1) return;
double sagitta = arcHeight;
// Bulge calculation: bulge = 2 * sagitta / chord
// For a CCW arc, bulge is positive.
double bulge = (2.0 * sagitta) / chordLength;
double Rm = (H * H) / (8.0 * w) + w / 2.0;
// Left side
double xInnerLeft = ox + w;
double xc_left = xInnerLeft + Rm - w;
Point2d C_m_L = new Point2d(xc_left, oy + H / 2.0);
Point2d C_fTop_L = new Point2d(xf_left, oy + H - r);
Point2d C_fBot_L = new Point2d(xf_left, oy + r);
double fx_L_tangTop = r > 0 ? C_m_L.X + (Rm / (Rm - r)) * (C_fTop_L.X - C_m_L.X) : xInnerLeft;
double fy_L_tangTop = r > 0 ? C_m_L.Y + (Rm / (Rm - r)) * (C_fTop_L.Y - C_m_L.Y) : oy + H;
double fx_L_tangBot = r > 0 ? C_m_L.X + (Rm / (Rm - r)) * (C_fBot_L.X - C_m_L.X) : xInnerLeft;
double fy_L_tangBot = r > 0 ? C_m_L.Y + (Rm / (Rm - r)) * (C_fBot_L.Y - C_m_L.Y) : oy;
// Left Arc: From Top to Bottom, bulging Left (CCW)
// Start: (xInnerLeft, yTopStraight)
// End: (xInnerLeft, yBottomStraight)
// Path: 12 o'clock -> 9 o'clock -> 6 o'clock direction is CCW.
var leftPoly = new Polyline();
leftPoly.AddVertexAt(0, new Point2d(xInnerLeft, yTopStraight), bulge, 0, 0);
leftPoly.AddVertexAt(1, new Point2d(xInnerLeft, yBottomStraight), 0, 0, 0);
if (r > 0.01)
{
double angTop1 = Math.PI / 2.0;
double angTop2 = Math.Atan2(fy_L_tangTop - C_fTop_L.Y, fx_L_tangTop - C_fTop_L.X);
double diffTop = angTop2 - angTop1;
while (diffTop < 0) diffTop += Math.PI * 2;
while (diffTop >= Math.PI * 2) diffTop -= Math.PI * 2;
double bulgeTop = Math.Tan(diffTop / 4.0);
double angM1 = Math.Atan2(fy_L_tangTop - C_m_L.Y, fx_L_tangTop - C_m_L.X);
double angM2 = Math.Atan2(fy_L_tangBot - C_m_L.Y, fx_L_tangBot - C_m_L.X);
double diffM = angM2 - angM1;
while (diffM < 0) diffM += Math.PI * 2;
while (diffM >= Math.PI * 2) diffM -= Math.PI * 2;
double bulgeM = Math.Tan(diffM / 4.0);
double angBot1 = Math.Atan2(fy_L_tangBot - C_fBot_L.Y, fx_L_tangBot - C_fBot_L.X);
double angBot2 = -Math.PI / 2.0;
double diffBot = angBot2 - angBot1;
while (diffBot < 0) diffBot += Math.PI * 2;
while (diffBot >= Math.PI * 2) diffBot -= Math.PI * 2;
double bulgeBot = Math.Tan(diffBot / 4.0);
leftPoly.AddVertexAt(0, new Point2d(xf_left, oy + H), bulgeTop, 0, 0);
leftPoly.AddVertexAt(1, new Point2d(fx_L_tangTop, fy_L_tangTop), bulgeM, 0, 0);
leftPoly.AddVertexAt(2, new Point2d(fx_L_tangBot, fy_L_tangBot), bulgeBot, 0, 0);
leftPoly.AddVertexAt(3, new Point2d(xf_left, oy), 0, 0, 0);
}
else
{
double angM1 = Math.Atan2(oy + H - C_m_L.Y, xInnerLeft - C_m_L.X);
double angM2 = Math.Atan2(oy - C_m_L.Y, xInnerLeft - C_m_L.X);
double diffM = angM2 - angM1;
while (diffM < 0) diffM += Math.PI * 2;
while (diffM >= Math.PI * 2) diffM -= Math.PI * 2;
double bulgeM = Math.Tan(diffM / 4.0);
leftPoly.AddVertexAt(0, new Point2d(xInnerLeft, oy + H), bulgeM, 0, 0);
leftPoly.AddVertexAt(1, new Point2d(xInnerLeft, oy), 0, 0, 0);
}
ctx.Style?.Apply(leftPoly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(leftPoly);
ctx.Tr.AddNewlyCreatedDBObject(leftPoly, true);
// Right Arc: From Bottom to Top, bulging Right (CCW)
// Start: (xInnerRight, yBottomStraight)
// End: (xInnerRight, yTopStraight)
// Path: 6 o'clock -> 3 o'clock -> 12 o'clock direction is CCW.
var leftClosureLine = new Line(new Point3d(fx_L_tangTop, fy_L_tangTop, 0), new Point3d(fx_L_tangBot, fy_L_tangBot, 0));
ctx.Style?.Apply(leftClosureLine, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(leftClosureLine);
ctx.Tr.AddNewlyCreatedDBObject(leftClosureLine, true);
// Right side
double xInnerRight = ox + totalWidth - w;
double xc_right = xInnerRight - Rm + w;
Point2d C_m_R = new Point2d(xc_right, oy + H / 2.0);
Point2d C_fTop_R = new Point2d(xf_right, oy + H - r);
Point2d C_fBot_R = new Point2d(xf_right, oy + r);
double fx_R_tangTop = r > 0 ? C_m_R.X + (Rm / (Rm - r)) * (C_fTop_R.X - C_m_R.X) : xInnerRight;
double fy_R_tangTop = r > 0 ? C_m_R.Y + (Rm / (Rm - r)) * (C_fTop_R.Y - C_m_R.Y) : oy + H;
double fx_R_tangBot = r > 0 ? C_m_R.X + (Rm / (Rm - r)) * (C_fBot_R.X - C_m_R.X) : xInnerRight;
double fy_R_tangBot = r > 0 ? C_m_R.Y + (Rm / (Rm - r)) * (C_fBot_R.Y - C_m_R.Y) : oy;
var rightPoly = new Polyline();
rightPoly.AddVertexAt(0, new Point2d(xInnerRight, yBottomStraight), bulge, 0, 0);
rightPoly.AddVertexAt(1, new Point2d(xInnerRight, yTopStraight), 0, 0, 0);
if (r > 0.01)
{
double angBot1 = -Math.PI / 2.0;
double angBot2 = Math.Atan2(fy_R_tangBot - C_fBot_R.Y, fx_R_tangBot - C_fBot_R.X);
double diffBot = angBot2 - angBot1;
while (diffBot < 0) diffBot += Math.PI * 2;
while (diffBot >= Math.PI * 2) diffBot -= Math.PI * 2;
double bulgeBot = Math.Tan(diffBot / 4.0);
double angM1 = Math.Atan2(fy_R_tangBot - C_m_R.Y, fx_R_tangBot - C_m_R.X);
double angM2 = Math.Atan2(fy_R_tangTop - C_m_R.Y, fx_R_tangTop - C_m_R.X);
double diffM = angM2 - angM1;
while (diffM < 0) diffM += Math.PI * 2;
while (diffM >= Math.PI * 2) diffM -= Math.PI * 2;
double bulgeM = Math.Tan(diffM / 4.0);
double angTop1 = Math.Atan2(fy_R_tangTop - C_fTop_R.Y, fx_R_tangTop - C_fTop_R.X);
double angTop2 = Math.PI / 2.0;
double diffTop = angTop2 - angTop1;
while (diffTop < 0) diffTop += Math.PI * 2;
while (diffTop >= Math.PI * 2) diffTop -= Math.PI * 2;
double bulgeTop = Math.Tan(diffTop / 4.0);
rightPoly.AddVertexAt(0, new Point2d(xf_right, oy), bulgeBot, 0, 0);
rightPoly.AddVertexAt(1, new Point2d(fx_R_tangBot, fy_R_tangBot), bulgeM, 0, 0);
rightPoly.AddVertexAt(2, new Point2d(fx_R_tangTop, fy_R_tangTop), bulgeTop, 0, 0);
rightPoly.AddVertexAt(3, new Point2d(xf_right, oy + H), 0, 0, 0);
}
else
{
double angM1 = Math.Atan2(oy - C_m_R.Y, xInnerRight - C_m_R.X);
double angM2 = Math.Atan2(oy + H - C_m_R.Y, xInnerRight - C_m_R.X);
double diffM = angM2 - angM1;
while (diffM < 0) diffM += Math.PI * 2;
while (diffM >= Math.PI * 2) diffM -= Math.PI * 2;
double bulgeM = Math.Tan(diffM / 4.0);
rightPoly.AddVertexAt(0, new Point2d(xInnerRight, oy), bulgeM, 0, 0);
rightPoly.AddVertexAt(1, new Point2d(xInnerRight, oy + H), 0, 0, 0);
}
ctx.Style?.Apply(rightPoly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(rightPoly);
ctx.Tr.AddNewlyCreatedDBObject(rightPoly, true);
var rightClosureLine = new Line(new Point3d(fx_R_tangTop, fy_R_tangTop, 0), new Point3d(fx_R_tangBot, fy_R_tangBot, 0));
ctx.Style?.Apply(rightClosureLine, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(rightClosureLine);
ctx.Tr.AddNewlyCreatedDBObject(rightClosureLine, true);
}
catch { }
}
@ -429,50 +525,21 @@ namespace CadParamPluging.Cad
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();
var topPoly = new Polyline();
topPoly.AddVertexAt(0, new Point2d(xStart, yTop), 0, 0, 0);
topPoly.AddVertexAt(1, new Point2d(xEnd, yTop), 0, 0, 0);
if (isLeft)
{
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);
}
poly.Closed = false;
}
else
{
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(topPoly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(topPoly);
ctx.Tr.AddNewlyCreatedDBObject(topPoly, true);
ctx.Style?.Apply(poly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(poly);
ctx.Tr.AddNewlyCreatedDBObject(poly, true);
var botPoly = new Polyline();
botPoly.AddVertexAt(0, new Point2d(xStart, yBottom), 0, 0, 0);
botPoly.AddVertexAt(1, new Point2d(xEnd, yBottom), 0, 0, 0);
ctx.Style?.Apply(botPoly, DrawingStyleManager.Role.OutlineBold);
ctx.Btr.AppendEntity(botPoly);
ctx.Tr.AddNewlyCreatedDBObject(botPoly, true);
}
private static void DrawRingSectionHatchWithFillet(FeatureDrivenDrawer.DrawingContext ctx, double xInnerRight, double xOuterRight, double yBottom, double height, double filletR)