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 даже когда вычитание прошло мимо тела, поэтому сервис сверяет объём до и после и откатывает операцию, если ничего не изменилось.
126 lines
5.5 KiB
C#
126 lines
5.5 KiB
C#
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); }
|
||
}
|
||
}
|