fix: правки draft по ревью Codex
- угол уклона вынесен в чистый SketchGeometry.RequireDraftAngle (0<angle<90, finite) + unit-тесты границ — P2 - описание инструмента уточнено: строго 0 < angle < 90 — P2 - интеграционный тест Draft_outward_expands_box — страхует маппинг направления (outward→!direction) — P2 - 115 тестов (69 unit + 46 integration)
This commit is contained in:
@@ -487,8 +487,7 @@ public sealed partial class PartModeler
|
||||
public Task<int> DraftAsync(IReadOnlyList<int> faceIndices, BasePlane neutralPlane, double angle, bool outward = false, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (!double.IsFinite(angle) || angle <= 0 || angle >= 90)
|
||||
throw new ArgumentOutOfRangeException(nameof(angle), "Угол уклона должен быть в (0; 90) градусов.");
|
||||
SketchGeometry.RequireDraftAngle(angle, nameof(angle));
|
||||
ArgumentNullException.ThrowIfNull(faceIndices);
|
||||
var indices = faceIndices.Distinct().ToList();
|
||||
if (indices.Count == 0) throw new ArgumentException("Нужна хотя бы одна грань для уклона.", nameof(faceIndices));
|
||||
|
||||
@@ -41,6 +41,13 @@ public static class SketchGeometry
|
||||
throw new ArgumentOutOfRangeException(paramName, value, $"Значение должно быть >= {min}.");
|
||||
}
|
||||
|
||||
/// <summary>Требовать угол уклона: конечный и строго в диапазоне (0; 90) градусов.</summary>
|
||||
public static void RequireDraftAngle(double angle, string paramName)
|
||||
{
|
||||
if (!double.IsFinite(angle) || angle <= 0 || angle >= 90)
|
||||
throw new ArgumentOutOfRangeException(paramName, angle, "Угол уклона должен быть в (0; 90) градусов.");
|
||||
}
|
||||
|
||||
/// <summary>Валидировать список точек: не null, минимум <paramref name="min"/>, все координаты конечны.</summary>
|
||||
public static void RequirePoints(IReadOnlyList<(double x, double y)> points, int min, string paramName)
|
||||
{
|
||||
|
||||
@@ -199,11 +199,11 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "draft")]
|
||||
[Description("Уклон граней: наклонить грани faceIndices (из list_faces) на угол angle (градусы, 0..90) относительно нейтральной (опорной) координатной плоскости neutralPlane (XOY|XOZ|YOZ) — сечение тела в этой плоскости остаётся неизменным, грани уклоняются вокруг линии пересечения с ней. outward=true — расширение (материал добавляется), false — сужение. Грани должны граничить/пересекаться с нейтральной плоскостью. Возвращает id операции.")]
|
||||
[Description("Уклон граней: наклонить грани faceIndices (из list_faces) на угол angle (градусы, строго 0 < angle < 90) относительно нейтральной (опорной) координатной плоскости neutralPlane (XOY|XOZ|YOZ) — сечение тела в этой плоскости остаётся неизменным, грани уклоняются вокруг линии пересечения с ней. outward=true — расширение (материал добавляется), false — сужение. Грани должны граничить/пересекаться с нейтральной плоскостью. Возвращает id операции.")]
|
||||
public async Task<string> Draft(
|
||||
[Description("Индексы уклоняемых граней из list_faces")] int[] faceIndices,
|
||||
[Description("Нейтральная (опорная) плоскость: XOY|XOZ|YOZ")] string neutralPlane,
|
||||
[Description("Угол уклона, градусы (0..90)")] double angle,
|
||||
[Description("Угол уклона, градусы (0 < angle < 90)")] double angle,
|
||||
[Description("Расширение (true) или сужение (false)")] bool outward = false)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
|
||||
@@ -314,6 +314,37 @@ public sealed class FeatureOpsTests
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Draft_outward_expands_box()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||||
try
|
||||
{
|
||||
// Та же коробка 40×40×20 (низ на Z=0).
|
||||
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
|
||||
await _modeler.AddRectangleAsync(s, -20, -20, 20, 20);
|
||||
await _modeler.CloseSketchAsync(s);
|
||||
await _modeler.ExtrudeAsync(s, depth: 20);
|
||||
var before = (await _query.GetPartInfoAsync()).Volume; // 32000
|
||||
|
||||
// 4 боковые грани под уклон 10° НАРУЖУ (outward=true) → верх расширяется, объём растёт.
|
||||
// Страхует маппинг направления (def.direction = !outward).
|
||||
var faces = await _query.ListFacesAsync();
|
||||
var sides = faces.Where(f => f.Type == "plane" && Math.Abs(f.Area - 800) < 1).Select(f => f.Index).ToArray();
|
||||
var id = await _modeler.DraftAsync(sides, BasePlane.XOY, angle: 10, outward: true);
|
||||
Assert.True(id > 0);
|
||||
await _modeler.RebuildAsync();
|
||||
|
||||
// Верх ≈ 40 + 2·20·tan10° = 47.05; V = (20/3)(1600 + 47.05² + √(1600·47.05²)) ≈ 37970.
|
||||
var after = (await _query.GetPartInfoAsync()).Volume;
|
||||
var top = 40 + 2 * 20 * Math.Tan(10 * Math.PI / 180);
|
||||
var expected = (20.0 / 3) * (1600 + top * top + Math.Sqrt(1600 * top * top));
|
||||
Assert.True(after > before, "Расширяющий уклон должен увеличить объём (маппинг outward).");
|
||||
Assert.InRange(after, expected * 0.9, expected * 1.1);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Loft_two_squares_makes_solid()
|
||||
{
|
||||
|
||||
@@ -53,6 +53,23 @@ public sealed class SketchGeometryTests
|
||||
Assert.Null(Record.Exception(() => SketchGeometry.RequireMin(5, 2, "count")));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(90)]
|
||||
[InlineData(-5)]
|
||||
[InlineData(120)]
|
||||
[InlineData(double.NaN)]
|
||||
[InlineData(double.PositiveInfinity)]
|
||||
public void RequireDraftAngle_rejects_out_of_range(double angle)
|
||||
=> Assert.Throws<ArgumentOutOfRangeException>(() => SketchGeometry.RequireDraftAngle(angle, "angle"));
|
||||
|
||||
[Theory]
|
||||
[InlineData(0.1)]
|
||||
[InlineData(45)]
|
||||
[InlineData(89.9)]
|
||||
public void RequireDraftAngle_passes_in_range(double angle)
|
||||
=> Assert.Null(Record.Exception(() => SketchGeometry.RequireDraftAngle(angle, "angle")));
|
||||
|
||||
[Fact]
|
||||
public void RequirePoints_throws_on_null()
|
||||
=> Assert.Throws<ArgumentNullException>(
|
||||
|
||||
Reference in New Issue
Block a user