Files
kompas3d-mcp/tests/Kompas.Mcp.Tests/Integration/PrimitiveTests.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

117 lines
5.1 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>
/// Интеграция: элементарные тела (API7 <c>IElementaryBodies</c>) — второй путь формообразования
/// рядом с «эскиз → выдавливание», в том числе вычитание примитива вместо выреза по эскизу.
/// </summary>
[Trait("Category", "Integration")]
[Collection(KompasCollection.Name)]
public sealed class PrimitiveTests : IntegrationTestBase
{
private readonly DocumentService _docs;
private readonly PrimitiveService _primitives;
private readonly PartModeler _modeler;
private readonly QueryService _query;
public PrimitiveTests(KompasFixture fx) : base(fx)
{
_docs = new DocumentService(fx.Session, fx.Dispatcher);
_primitives = new PrimitiveService(fx.Session, fx.Dispatcher);
_modeler = new PartModeler(fx.Session, fx.Dispatcher);
_query = new QueryService(fx.Session, fx.Dispatcher);
}
[Fact]
public async Task Block_is_built_by_sizes_from_the_anchor_corner()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
await _primitives.PrimitiveAsync(PrimitiveKind.Block, x: 4, y: 0, z: 0,
length: 82, width: 10.5, height: 3);
await _modeler.RebuildAsync();
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.MinX, 3.99, 4.01); // точка привязки — угол, а не центр
Assert.InRange(box.SizeX, 81.9, 82.1);
Assert.InRange(box.SizeY, 10.4, 10.6);
Assert.InRange(box.SizeZ, 2.99, 3.01);
var v = (await _query.GetPartInfoAsync()).Volume; // 82·10.5·3 = 2583
Assert.InRange(v, 2583 * 0.99, 2583 * 1.01);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Subtracted_block_hollows_a_pocket_without_a_sketch()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
await _primitives.PrimitiveAsync(PrimitiveKind.Block, 0, 0, 0, length: 40, width: 20, height: 10);
// Карман 30×10 глубиной 4 — вычитаем блок, приподняв его до дна кармана.
await _primitives.PrimitiveAsync(PrimitiveKind.Block, 5, 5, 6, length: 30, width: 10, height: 10,
result: PrimitiveResult.Subtract);
await _modeler.RebuildAsync();
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.SizeZ, 9.9, 10.1); // габарит не изменился — вырезано внутри
var v = (await _query.GetPartInfoAsync()).Volume; // 40·20·10 30·10·4 = 6800
Assert.InRange(v, 6800 * 0.99, 6800 * 1.01);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Cylinder_can_be_added_to_an_existing_body()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
await _primitives.PrimitiveAsync(PrimitiveKind.Block, 0, 0, 0, length: 20, width: 20, height: 5);
await _primitives.PrimitiveAsync(PrimitiveKind.Cylinder, 10, 10, 5, diameter: 8, height: 10,
result: PrimitiveResult.Union);
await _modeler.RebuildAsync();
var bodies = await _query.ListFacesAsync();
Assert.NotEmpty(bodies);
var box = await _query.GetBoundingBoxAsync();
Assert.InRange(box.SizeZ, 14.9, 15.1); // 5 плиты + 10 цилиндра
var v = (await _query.GetPartInfoAsync()).Volume; // 20·20·5 + π·4²·10 ≈ 2502.7
var expected = 20 * 20 * 5 + Math.PI * 16 * 10;
Assert.InRange(v, expected * 0.98, expected * 1.02);
}
finally { await _docs.CloseAsync(save: false); }
}
[Fact]
public async Task Subtracting_where_nothing_intersects_fails_with_a_hint()
{
await _docs.CreateAsync(KompasDocumentType.Part);
try
{
await _primitives.PrimitiveAsync(PrimitiveKind.Block, 0, 0, 0, length: 10, width: 10, height: 10);
// Update() при вычитании мимо тела возвращает TRUE — ловим по неизменившемуся объёму.
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
_primitives.PrimitiveAsync(PrimitiveKind.Sphere, 100, 100, 100, diameter: 5,
result: PrimitiveResult.Subtract));
Assert.Contains("не удалило материал", ex.Message, StringComparison.Ordinal);
// Неудачная операция не должна оставаться в дереве: объём прежний, тело одно.
var v = (await _query.GetPartInfoAsync()).Volume;
Assert.InRange(v, 1000 * 0.99, 1000 * 1.01);
}
finally { await _docs.CloseAsync(save: false); }
}
}