Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/SketchTextTests.cs
T
mikhail a4cc755fb4 Формообразование: надписи в эскизе, тонкая стенка, тела по размерам
Три способа получить форму там, где раньше был только замкнутый контур
из отрезков и дуг.

1. Примитив эскиза type=text: ksTextEx + ksConvertTextToCurve. Пока текст
   остаётся текстом, это оформление, и операция его не видит; после
   конвертации глифы становятся обычным сечением. Ответ возвращает
   фактическую длину строки — иначе ширину шрифта до выдавливания не узнать.

2. extrude(thinThickness, thinSide): контур трактуется как стенка заданной
   толщины. outward по замкнутому контуру даёт кайму-эквидистанту вокруг
   него — так надпись получает подложку без 2D-эквидистанты.
   Толщину при dtReverse КОМПАС читает из reverseThickness: положить её в
   normalThickness значит молча получить СПЛОШНОЕ сечение вместо рамки.

3. Инструмент primitive: элементарные тела API7 (block, cylinder, sphere,
   cone) с result=new|union|subtract|intersect. Карман и паз строятся
   вычитанием тела, без эскиза и выреза. Update() возвращает TRUE даже
   когда вычитание прошло мимо тела, поэтому сервис сверяет объём до и
   после и откатывает операцию, если ничего не изменилось.
2026-07-31 14:50:56 +03:00

126 lines
5.5 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);
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 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); }
}
}