Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/DrawingAssociativeDimensionTests.cs
mikhail 775128484e feat(drawing): ассоциативная привязка диаметрального/радиального размера к окружности вида
Флаг associate у drawing_add_diametral_dimension/drawing_add_radial_dimension:
- associate=false (по умолчанию) — прежний свободный размер (без изменений);
- associate=true — (xc,yc,radius) как ключ поиска спроецированной окружности
  (IDrawingContainer.Circles), привязка через BaseObject; значение Ø/R читается
  из геометрии и обновляется с моделью.

- CircularObjectMatch.SelectMatchIndex (чистый отбор по центру+радиусу, допуск 1 мм;
  бросает при отсутствии/неоднозначности; концентрические разделяются радиусом)
- RequireViewContainers (вид → ISymbols2DContainer + IDrawingContainer), CollectCircles, FindBaseCircle
- 5 unit + 6 интеграционных тестов (всего 278 зелёных), сборка Release чистая

Спайк подтвердил: BaseObject=circle без задания Xc/Yc/Radius → Valid, NominalValue из
геометрии (Ø=20/R=10 для R10). Объём — только окружности (Circles); дуги/угловой/линейный —
далее. Ревью Codex спека учтено (оба контейнера, guard вокруг блока, доказательство
ассоциативности тестом radius=10.3→Ø20, регресс обоих свободных путей).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:47:17 +03:00

144 lines
6.2 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 Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Drawings;
using Kompas.Mcp.Core.Modeling;
namespace Kompas.Mcp.Tests.Integration;
/// <summary>Интеграция: ассоциативная привязка диаметрального/радиального размера к окружности вида
/// (BaseObject). Модель — цилиндр R10 (вид проецирует окружность).</summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class DrawingAssociativeDimensionTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PartModeler _modeler;
private readonly DrawingService _drawing;
public DrawingAssociativeDimensionTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_drawing = new DrawingService(fx.Session, fx.Dispatcher);
}
private async Task<string> BuildAndSaveCylinderAsync()
{
var path = TestPaths.NewFile(".m3d");
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddCircleAsync(s, 0, 0, 10);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: 20);
await _modeler.RebuildAsync();
await _docs.SaveAsAsync(path);
}
finally { await _docs.CloseAsync(save: false); }
return path;
}
[Fact]
public async Task Diametral_associative_reads_diameter_from_geometry()
{
var partFile = await BuildAndSaveCylinderAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
var target = views.ViewNumbers[0]; // вид с окружностью (спайк)
var before = await _drawing.GetViewDiametralDimensionCountAsync(target);
// Ключ radius=10.3 (в допуске 1.0 находит окружность R10). Ассоциативно → Ø=20 из геометрии,
// а НЕ 2·10.3=20.6 → доказывает чтение значения из геометрии, а не из входа.
var r = await _drawing.AddDiametralDimensionAsync(target, xc: 0, yc: 0, radius: 10.3,
angleDeg: 45, associate: true);
var after = await _drawing.GetViewDiametralDimensionCountAsync(target);
Assert.Equal(target, r.ViewNumber);
Assert.InRange(r.Value, 19.9, 20.1); // Ø=20 из геометрии (не 20.6)
Assert.Equal(before + 1, after);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Radial_associative_reads_radius_from_geometry()
{
var partFile = await BuildAndSaveCylinderAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
var target = views.ViewNumbers[0];
var r = await _drawing.AddRadialDimensionAsync(target, xc: 0, yc: 0, radius: 10.3,
angleDeg: -45, associate: true);
Assert.InRange(r.Value, 9.9, 10.1); // R=10 из геометрии (не 10.3)
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Associative_no_circle_near_throws_and_no_orphan()
{
var partFile = await BuildAndSaveCylinderAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
var target = views.ViewNumbers[0];
var before = await _drawing.GetViewDiametralDimensionCountAsync(target);
// Ключ далеко от любой окружности → отбор бросит до Add (размер не создаётся).
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_drawing.AddDiametralDimensionAsync(target, xc: 1000, yc: 1000, radius: 5,
angleDeg: 0, associate: true));
var after = await _drawing.GetViewDiametralDimensionCountAsync(target);
Assert.Equal(before, after); // нет «осиротевшего» размера
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Free_diametral_path_unchanged()
{
var partFile = await BuildAndSaveCylinderAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
// associate=false (по умолчанию) — свободный размер: значение = 2·radius.
var r = await _drawing.AddDiametralDimensionAsync(views.ViewNumbers[0], xc: 0, yc: 0,
radius: 15, angleDeg: 0, associate: false);
Assert.InRange(r.Value, 29.9, 30.1);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Free_radial_path_unchanged()
{
var partFile = await BuildAndSaveCylinderAsync();
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
var views = await _drawing.CreateStandardViewsAsync(partFile, 1.0, 100, 150);
var r = await _drawing.AddRadialDimensionAsync(views.ViewNumbers[0], xc: 0, yc: 0,
radius: 12, angleDeg: 0, associate: false);
Assert.InRange(r.Value, 11.9, 12.1);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Associative_no_views_throws()
{
await _docs.CreateAsync(KompasDocumentType.Drawing);
try
{
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_drawing.AddDiametralDimensionAsync(0, xc: 0, yc: 0, radius: 10, angleDeg: 0, associate: true));
}
finally { await _docs.CloseAsync(save: false); }
}
}