Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/SketchTextTests.cs
T
mikhail 31b933e1cc Ответ надписи — габарит глифов; запись на диск создаёт каталог
ksConvertTextToCurve ВОЗВРАЩАЕТ ссылку на кривые, и только по ней ksGetObjGabaritRect
даёт габарит букв: по текстовой ссылке это ячейка строки (Arial h=10 «HH» — 23.75 против
19.97 у глифов, второе совпадает с выдавленным телом). Агент вписывает надпись в поле
по габариту, поэтому в ответе теперь оба числа.

OutputPath.Prepare создаёт каталог назначения и делает путь абсолютным (document_save,
export_step, снимок): КОМПАС в несуществующую папку молча не пишет, а диагностика при
этом винила занятое имя файла — чинили не ту причину. Причины разведены.
2026-07-31 16:36:01 +03:00

157 lines
7.2 KiB
C#
Raw 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.Modeling;
using Kompas.Mcp.Core.Query;
namespace Kompas.Mcp.Tests.Integration;
/// <summary>
/// Интеграция: надпись в эскизе. Текст сам по себе — оформление, выдавить его нельзя;
/// примитив type=text переводит его в кривые (ksConvertTextToCurve), и контур глифов
/// становится обычным сечением операции.
/// </summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class SketchTextTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PartModeler _modeler;
private readonly QueryService _query;
public SketchTextTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_query = new QueryService(fx.Session, fx.Dispatcher);
}
private static SketchEntity Text(string text, double x, double y, double height, string? font = null) => new()
{
Kind = SketchEntityKind.Text,
Points = [(x, y)],
Text = text,
Height = height,
FontName = font,
};
[Fact]
public async Task Text_becomes_curves_and_extrudes()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
var notes = await _modeler.AddEntitiesAsync(s, [Text("HI", 0, 0, 10, "Arial")], autoClose: true);
await _modeler.ExtrudeAsync(s, depth: 2);
await _modeler.RebuildAsync();
// Метрики нужны агенту для выравнивания: до выдавливания их узнать неоткуда.
Assert.Contains("глифы", notes[0], StringComparison.Ordinal);
Assert.Contains("ширина ячейки", notes[0], StringComparison.Ordinal);
var info = await _query.GetPartInfoAsync();
Assert.True(info.Volume > 0, "надпись не дала объёма — текст не переведён в кривые");
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.SizeZ, 1.99, 2.01);
Assert.InRange(box.SizeY, 6, 11); // высота прописных ≈ height
Assert.True(box.SizeX > box.SizeY); // «HI» шире, чем выше
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Reported_glyph_box_matches_the_extruded_geometry()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
// Ширина ячейки (её и возвращали раньше) больше габарита глифов, поэтому агент,
// вписывающий надпись в поле, промахивался. Сверяем заявленный габарит с фактическим.
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
var notes = await _modeler.AddEntitiesAsync(s, [Text("HH", 0, 0, 10, "Arial")], autoClose: true);
await _modeler.ExtrudeAsync(s, depth: 2);
await _modeler.RebuildAsync();
var numbers = System.Text.RegularExpressions.Regex
.Matches(notes[0], @"-?\d+\.\d{2}")
.Select(m => double.Parse(m.Value, System.Globalization.CultureInfo.InvariantCulture))
.ToList();
Assert.True(numbers.Count >= 7, $"в заметке нет чисел габарита: {notes[0]}");
var (minX, maxX, minY, maxY) = (numbers[0], numbers[1], numbers[2], numbers[3]);
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(maxX - minX, box.SizeX - 0.05, box.SizeX + 0.05);
Assert.InRange(maxY - minY, box.SizeY - 0.05, box.SizeY + 0.05);
// Единицы — миллиметры эскиза, а не сантиметры листа: высота прописной ≈ height.
Assert.InRange(maxY - minY, 9.5, 10.5);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Unknown_font_is_reported_by_name()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
var bad = Text("HI", 0, 0, 10, "No Such Font 12345");
// КОМПАС подставляет замену вместо отсутствующего шрифта, поэтому надпись
// всё равно строится — важно, что она не рушит пакет и даёт геометрию.
var notes = await _modeler.AddEntitiesAsync(s, [bad], autoClose: true);
Assert.False(string.IsNullOrEmpty(notes[0]));
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Thin_wall_outward_grows_the_contour()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
// Квадрат 20×20 стенкой 2 мм наружу: габарит вырастает на 2·2 мм по каждой оси,
// а объём равен объёму рамки, а не сплошной плиты.
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddRectangleAsync(s, 0, 0, 20, 20);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: 5, thinThickness: 2, thinSide: ThinSide.Outward);
await _modeler.RebuildAsync();
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.SizeX, 23.9, 24.1);
Assert.InRange(box.SizeY, 23.9, 24.1);
var v = (await _query.GetPartInfoAsync()).Volume; // (24² 20²)·5 = 880
Assert.InRange(v, 880 * 0.98, 880 * 1.02);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Thin_wall_inward_keeps_the_contour_and_hollows_it()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
// Стенка внутрь контура: габарит остаётся 20×20, материал — только рамка по периметру.
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
await _modeler.AddRectangleAsync(s, 0, 0, 20, 20);
await _modeler.CloseSketchAsync(s);
await _modeler.ExtrudeAsync(s, depth: 5, thinThickness: 2, thinSide: ThinSide.Inward);
await _modeler.RebuildAsync();
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.SizeX, 19.9, 20.1);
Assert.InRange(box.SizeY, 19.9, 20.1);
// (20² 16²)·5 = 720; сплошная плита дала бы 2000 — так ловится «стенка не применилась».
var v = (await _query.GetPartInfoAsync()).Volume;
Assert.InRange(v, 720 * 0.98, 720 * 1.02);
}
finally { await _docs.CloseAsync(save: false); }
}
}