feat(feature): операция Оболочка (shell)
ShellAsync (o3d_shellOperation): удаление граней по индексам + толщина стенки, thinType=!outward. SelectFaceByIndex в ядре, инструмент shell в FeatureTools. Валидация: thickness>0, дедупликация индексов, проверка диапазона до мутации. Integration-тест: коробка 40x30x20 → оболочка 2мм → 7152 мм³ (±5%). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -196,6 +196,40 @@ public sealed partial class PartModeler
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>Превратить тело в оболочку: удалить (открыть) грани с индексами faceIndices,
|
||||
/// задать толщину стенки thickness (мм). outward=false — толщина внутрь (габарит сохраняется),
|
||||
/// true — наружу. Возвращает id операции.</summary>
|
||||
public Task<int> ShellAsync(IReadOnlyList<int> faceIndices, double thickness, bool outward = false, CancellationToken ct = default)
|
||||
=> _dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (thickness <= 0) throw new ArgumentOutOfRangeException(nameof(thickness), "Толщина должна быть > 0.");
|
||||
ArgumentNullException.ThrowIfNull(faceIndices);
|
||||
var indices = faceIndices.Distinct().ToList();
|
||||
if (indices.Count == 0) throw new ArgumentException("Нужна хотя бы одна удаляемая грань.", nameof(faceIndices));
|
||||
|
||||
var part = GetTopPart();
|
||||
// Проверяем диапазон ВСЕХ индексов до мутации модели.
|
||||
var faces = indices.Select(i => SelectFaceByIndex(part, i)).ToList();
|
||||
|
||||
var entity = part.NewEntity((short)Obj3dType.o3d_shellOperation) as ksEntity
|
||||
?? throw new InvalidOperationException("NewEntity(o3d_shellOperation) вернул null.");
|
||||
var def = entity.GetDefinition() as ksShellDefinition
|
||||
?? throw new InvalidOperationException("GetDefinition() оболочки вернул не ksShellDefinition.");
|
||||
var arr = def.FaceArray() as ksEntityCollection
|
||||
?? throw new InvalidOperationException("FaceArray() оболочки вернул не ksEntityCollection.");
|
||||
foreach (var f in faces) arr.Add(f);
|
||||
def.thickness = thickness;
|
||||
def.thinType = !outward; // true=внутрь, false=наружу
|
||||
|
||||
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(() =>
|
||||
|
||||
@@ -80,6 +80,18 @@ public sealed partial class PartModeler : IDisposable
|
||||
?? throw new InvalidOperationException("Ребро из коллекции не приводится к ksEntity.");
|
||||
}
|
||||
|
||||
/// <summary>Выбрать грань детали по индексу из list_faces (с проверкой диапазона).</summary>
|
||||
private static ksEntity SelectFaceByIndex(ksPart part, int faceIndex)
|
||||
{
|
||||
var faces = part.EntityCollection((short)Obj3dType.o3d_face) as ksEntityCollection
|
||||
?? throw new InvalidOperationException("Не удалось получить коллекцию граней.");
|
||||
if (faceIndex < 0 || faceIndex >= faces.GetCount())
|
||||
throw new ArgumentOutOfRangeException(nameof(faceIndex),
|
||||
$"Индекс грани вне диапазона [0; {faces.GetCount() - 1}]. Сверьтесь с list_faces.");
|
||||
return faces.GetByIndex(faceIndex) as ksEntity
|
||||
?? throw new InvalidOperationException("Грань по индексу не приводится к ksEntity.");
|
||||
}
|
||||
|
||||
/// <summary>Выбрать ребро детали по индексу из list_edges.</summary>
|
||||
private static ksEntity SelectEdgeByIndex(ksPart part, int edgeIndex)
|
||||
{
|
||||
|
||||
@@ -91,6 +91,18 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler)
|
||||
return $"Фаска {distance}×{distance} создана на ребре [{edgeIndex}], id={id}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "shell")]
|
||||
[Description("Превратить тело активной детали в оболочку (придать стенкам толщину): удалить (открыть) грани с индексами faceIndices из list_faces и задать толщину стенки thickness (мм). outward=false — толщина внутрь (габарит сохраняется), true — наружу. Возвращает id операции.")]
|
||||
public async Task<string> Shell(
|
||||
[Description("Индексы удаляемых (открываемых) граней из list_faces")] int[] faceIndices,
|
||||
[Description("Толщина стенки, мм")] double thickness,
|
||||
[Description("Толщина наружу (true) или внутрь (false)")] bool outward = false)
|
||||
{
|
||||
await session.ConnectAsync();
|
||||
var id = await modeler.ShellAsync(faceIndices, thickness, outward);
|
||||
return $"Оболочка толщиной {thickness} мм создана (удалено граней: {faceIndices.Length}), id={id}.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "rebuild")]
|
||||
[Description("Перестроить активный документ.")]
|
||||
public async Task<string> Rebuild()
|
||||
|
||||
Reference in New Issue
Block a user