feat: круговой массив (circular_pattern) — ksCircularCopyDefinition o3d_circularCopy
- CircularPatternAsync: SetAxis + свойства напрямую (count1=1 радиально, count2=count кольцевое, step2 угол°, factor2=false, inverce=reverse), GetOperationArray - инструмент circular_pattern (axis, count, step°, reverse, geometric) - интеграционный тест (3 экз. вокруг Z, шаг 90°, объём ±5% + габарит подтверждает поворот) - эмпирически: count1=1/count2=N даёт чистый круговой массив, step2 в градусах между соседними
This commit is contained in:
@@ -371,6 +371,54 @@ public sealed partial class PartModeler
|
||||
return id;
|
||||
}, ct);
|
||||
|
||||
/// <summary>Круговой массив: размножить операции featureIds вокруг координатной оси axis,
|
||||
/// count экземпляров всего (включая исходный, ≥2), угловой шаг step (градусы) между соседними.
|
||||
/// reverse — обратное направление вращения. geometric=true — быстрое геометрическое копирование.
|
||||
/// Возвращает id операции.</summary>
|
||||
public Task<int> CircularPatternAsync(IReadOnlyList<int> featureIds, CoordinateAxis axis, int count, double step, bool reverse = false, bool geometric = false, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(featureIds);
|
||||
var ids = featureIds.Distinct().ToList();
|
||||
if (ids.Count == 0) throw new ArgumentException("Нужна хотя бы одна операция для массива.", nameof(featureIds));
|
||||
SketchGeometry.RequireMin(count, 2, nameof(count)); // count включает исходный
|
||||
SketchGeometry.RequirePositive(step, nameof(step)); // угловой шаг > 0, ловит NaN
|
||||
|
||||
// Собираем источники до мутации модели (проверка существования всех id).
|
||||
var features = ids.Select(RequireFeature).ToList();
|
||||
|
||||
var part = GetTopPart();
|
||||
var axisEntity = part.GetDefaultEntity((short)CoordinateAxes.ToObj3dType(axis)) as ksEntity
|
||||
?? throw new InvalidOperationException("Не удалось получить координатную ось.");
|
||||
|
||||
var entity = part.NewEntity((short)Obj3dType.o3d_circularCopy) as ksEntity
|
||||
?? throw new InvalidOperationException("NewEntity(o3d_circularCopy) вернул null.");
|
||||
var def = entity.GetDefinition() as ksCircularCopyDefinition
|
||||
?? throw new InvalidOperationException("GetDefinition() массива вернул не ksCircularCopyDefinition.");
|
||||
def.SetAxis(axisEntity);
|
||||
// Чистый круговой массив: радиальное направление выключено (count1=1),
|
||||
// экземпляры идут по кольцу (count2=count) с угловым шагом step° между соседними.
|
||||
def.count1 = 1;
|
||||
def.step1 = 0;
|
||||
def.factor1 = false;
|
||||
def.count2 = count;
|
||||
def.step2 = step;
|
||||
def.factor2 = false; // шаг между соседними (не общий угол)
|
||||
def.inverce = reverse;
|
||||
def.geomArray = geometric;
|
||||
var arr = def.GetOperationArray() as ksEntityCollection
|
||||
?? throw new InvalidOperationException("GetOperationArray() массива вернул не ksEntityCollection.");
|
||||
foreach (var f in features) arr.Add(f);
|
||||
|
||||
if (!entity.Create())
|
||||
throw new InvalidOperationException(
|
||||
"Create() кругового массива вернул FALSE (копии самопересекаются или неверная ось?).");
|
||||
|
||||
var id = _nextId++;
|
||||
_features[id] = entity;
|
||||
return id;
|
||||
}, ct);
|
||||
|
||||
/// <summary>Перестроить документ.</summary>
|
||||
public Task RebuildAsync(CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
|
||||
@@ -148,6 +148,21 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler)
|
||||
return $"Линейный массив создан ({count} экз. вдоль {axis}, шаг {step} мм), id={id}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "circular_pattern")]
|
||||
[Description("Круговой массив: размножить ранее созданные операции featureIds вокруг координатной оси axis (X|Y|Z). count — количество экземпляров ВСЕГО, включая исходный (>=2). step — угловой шаг между соседними экземплярами, градусы (например 90 для 4 шт через 90°, или 120 для 3 шт). reverse=true — обратное направление вращения. geometric=true — быстрое геометрическое копирование. Возвращает id операции.")]
|
||||
public async Task<string> CircularPattern(
|
||||
[Description("Id операций-источников")] int[] featureIds,
|
||||
[Description("Координатная ось вращения: X|Y|Z")] string axis,
|
||||
[Description("Число экземпляров всего, включая исходный (>=2)")] int count,
|
||||
[Description("Угловой шаг между соседними, градусы")] double step,
|
||||
[Description("Обратное направление вращения")] bool reverse = false,
|
||||
[Description("Геометрическое копирование (быстрее)")] bool geometric = false)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var id = await modeler.CircularPatternAsync(featureIds, CoordinateAxes.Parse(axis), count, step, reverse, geometric);
|
||||
return $"Круговой массив создан ({count} экз. вокруг {axis}, шаг {step}°), id={id}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "rebuild")]
|
||||
[Description("Перестроить активный документ.")]
|
||||
public async Task<string> Rebuild()
|
||||
|
||||
@@ -165,6 +165,35 @@ public sealed class FeatureOpsTests
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CircularPattern_replicates_cylinder_around_Z()
|
||||
{
|
||||
await _docs.CreateAsync(KompasDocumentType.Part);
|
||||
try
|
||||
{
|
||||
// Цилиндр R3×H10 со смещением: центр (20,0) (объём π·9·10 ≈ 282.7 мм³).
|
||||
var s = await _modeler.OpenSketchAsync(BasePlane.XOY);
|
||||
await _modeler.AddCircleAsync(s, 20, 0, 3);
|
||||
await _modeler.CloseSketchAsync(s);
|
||||
var cyl = await _modeler.ExtrudeAsync(s, depth: 10);
|
||||
var one = (await _query.GetPartInfoAsync()).Volume;
|
||||
|
||||
// Круговой массив вокруг Z: 3 экз. с шагом 90° → центры (20,0),(0,20),(-20,0).
|
||||
var id = await _modeler.CircularPatternAsync(new[] { cyl }, CoordinateAxis.Z, count: 3, step: 90);
|
||||
Assert.True(id > 0);
|
||||
await _modeler.RebuildAsync();
|
||||
|
||||
var v = (await _query.GetPartInfoAsync()).Volume;
|
||||
Assert.InRange(v, one * 3 * 0.95, one * 3 * 1.05);
|
||||
|
||||
// Габарит: X от -23 до 23 → 46; Y от -3 до 23 → 26 (поворот реально произошёл).
|
||||
var box = await _query.GetBoundingBoxAsync();
|
||||
Assert.InRange(box.SizeX, 46 * 0.95, 46 * 1.05);
|
||||
Assert.InRange(box.SizeY, 26 * 0.95, 26 * 1.05);
|
||||
}
|
||||
finally { await _docs.CloseAsync(save: false); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Loft_two_squares_makes_solid()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user