using System.Runtime.Versioning;
using Kompas6API5;
using Kompas6Constants3D;
namespace Kompas.Mcp.Core.Modeling;
/// PartModeler: формообразующие операции (выдавливание, вращение, скругление, фаска).
[SupportedOSPlatform("windows")]
public sealed partial class PartModeler
{
///
/// Выдавить эскиз. — вырезание вместо добавления материала.
/// Возвращает id операции.
///
/// Сквозное выдавливание/вырез через всю деталь в обе стороны
/// (для отверстий — гарантированно пробивает материал; игнорируется).
public Task ExtrudeAsync(
int sketchId, double depth, bool cut = false, bool forward = true,
double draftAngle = 0, bool throughAll = false, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
if (!throughAll && depth <= 0)
throw new ArgumentOutOfRangeException(nameof(depth), "Глубина должна быть > 0 (или используйте throughAll).");
var sketch = RequireSketch(sketchId);
CloseSketchCore(sketch); // эскиз должен быть закрыт перед операцией
var part = GetTopPart();
var type = cut ? Obj3dType.o3d_cutExtrusion : Obj3dType.o3d_bossExtrusion;
var entity = part.NewEntity((short)type) as ksEntity
?? throw new InvalidOperationException("NewEntity операции выдавливания вернул null.");
// Конкретные типы определений (без dynamic): ошибки связывания — на этапе компиляции.
if (cut)
{
var def = entity.GetDefinition() as ksCutExtrusionDefinition
?? throw new InvalidOperationException("GetDefinition() выреза вернул не ksCutExtrusionDefinition.");
if (throughAll)
{
def.directionType = (short)Direction_Type.dtBoth;
def.SetSideParam(true, (short)End_Type.etThroughAll, depth, draftAngle, false);
def.SetSideParam(false, (short)End_Type.etThroughAll, depth, draftAngle, false);
}
else
{
def.directionType = (short)Direction_Type.dtNormal;
def.SetSideParam(forward, (short)End_Type.etBlind, depth, draftAngle, false);
}
def.SetSketch(sketch.Entity);
}
else
{
if (throughAll)
throw new ArgumentException("throughAll поддерживается только для выреза (cut).", nameof(throughAll));
var def = entity.GetDefinition() as ksBossExtrusionDefinition
?? throw new InvalidOperationException("GetDefinition() выдавливания вернул не ksBossExtrusionDefinition.");
def.directionType = (short)Direction_Type.dtNormal;
def.SetSideParam(forward, (short)End_Type.etBlind, depth, draftAngle, false);
def.SetSketch(sketch.Entity);
}
if (!entity.Create())
throw new InvalidOperationException("Create() операции выдавливания вернул FALSE.");
var id = _nextId++;
_features[id] = entity;
return id;
}, ct);
///
/// Вращать эскиз вокруг его осевой линии (добавьте её через AddAxis). —
/// вырезание вместо добавления материала. Угол — в градусах.
/// Возвращает id операции.
///
public Task RevolveAsync(
int sketchId, double angle = 360, bool cut = false, bool forward = true,
CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
if (angle is <= 0 or > 360)
throw new ArgumentOutOfRangeException(nameof(angle), "Угол вращения должен быть в (0; 360].");
var sketch = RequireSketch(sketchId);
CloseSketchCore(sketch); // эскиз должен быть закрыт перед операцией
var part = GetTopPart();
var type = cut ? Obj3dType.o3d_cutRotated : Obj3dType.o3d_bossRotated;
var entity = part.NewEntity((short)type) as ksEntity
?? throw new InvalidOperationException("NewEntity операции вращения вернул null.");
if (cut)
{
var def = entity.GetDefinition() as ksCutRotatedDefinition
?? throw new InvalidOperationException("GetDefinition() выреза вращением вернул не ksCutRotatedDefinition.");
def.directionType = (short)Direction_Type.dtNormal;
def.SetThinParam(false, 0, 0, 0);
def.SetSideParam(forward, angle);
def.SetSketch(sketch.Entity);
}
else
{
var def = entity.GetDefinition() as ksBossRotatedDefinition
?? throw new InvalidOperationException("GetDefinition() вращения вернул не ksBossRotatedDefinition.");
def.directionType = (short)Direction_Type.dtNormal;
def.toroidShapeType = false; // сфероид (сплошное тело), не тороид
def.SetThinParam(false, 0, 0, 0);
def.SetSideParam(forward, angle);
def.SetSketch(sketch.Entity);
}
if (!entity.Create())
throw new InvalidOperationException("Create() операции вращения вернул FALSE.");
var id = _nextId++;
_features[id] = entity;
return id;
}, ct);
///
/// Скруглить ребро детали, найденное по точке (мировые координаты, мм), радиусом .
/// Возвращает id операции.
///
public Task FilletEdgeAsync(double x, double y, double z, double radius, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var part = GetTopPart();
return CreateFillet(part, SelectEdgeByPoint(part, x, y, z), radius);
}, ct);
/// Скруглить ребро детали по индексу из list_edges радиусом .
public Task FilletEdgeIndexAsync(int edgeIndex, double radius, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var part = GetTopPart();
return CreateFillet(part, SelectEdgeByIndex(part, edgeIndex), radius);
}, ct);
private int CreateFillet(ksPart part, ksEntity edge, double radius)
{
if (radius <= 0) throw new ArgumentOutOfRangeException(nameof(radius), "Радиус скругления должен быть > 0.");
var entity = part.NewEntity((short)Obj3dType.o3d_fillet) as ksEntity
?? throw new InvalidOperationException("NewEntity(o3d_fillet) вернул null.");
var def = entity.GetDefinition() as ksFilletDefinition
?? throw new InvalidOperationException("GetDefinition() скругления вернул не ksFilletDefinition.");
def.radius = radius;
def.tangent = false;
var arr = def.array() as ksEntityCollection
?? throw new InvalidOperationException("array() скругления вернул не ksEntityCollection.");
arr.Add(edge);
if (!entity.Create())
throw new InvalidOperationException("Create() скругления вернул FALSE (радиус слишком велик для ребра?).");
var id = _nextId++;
_features[id] = entity;
return id;
}
///
/// Снять фаску с ребра детали, найденного по точке (мировые координаты, мм), катетом .
/// Возвращает id операции.
///
public Task ChamferEdgeAsync(double x, double y, double z, double distance, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var part = GetTopPart();
return CreateChamfer(part, SelectEdgeByPoint(part, x, y, z), distance);
}, ct);
/// Снять фаску с ребра детали по индексу из list_edges катетом .
public Task ChamferEdgeIndexAsync(int edgeIndex, double distance, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var part = GetTopPart();
return CreateChamfer(part, SelectEdgeByIndex(part, edgeIndex), distance);
}, ct);
private int CreateChamfer(ksPart part, ksEntity edge, double distance)
{
if (distance <= 0) throw new ArgumentOutOfRangeException(nameof(distance), "Катет фаски должен быть > 0.");
var entity = part.NewEntity((short)Obj3dType.o3d_chamfer) as ksEntity
?? throw new InvalidOperationException("NewEntity(o3d_chamfer) вернул null.");
var def = entity.GetDefinition() as ksChamferDefinition
?? throw new InvalidOperationException("GetDefinition() фаски вернул не ksChamferDefinition.");
def.tangent = false;
def.SetChamferParam(true, distance, distance);
var arr = def.array() as ksEntityCollection
?? throw new InvalidOperationException("array() фаски вернул не ksEntityCollection.");
arr.Add(edge);
if (!entity.Create())
throw new InvalidOperationException("Create() фаски вернул FALSE (катет слишком велик для ребра?).");
var id = _nextId++;
_features[id] = entity;
return id;
}
/// Перестроить документ.
public Task RebuildAsync(CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() =>
{
var doc3d = ActiveDoc3D();
doc3d.RebuildDocument();
}, ct);
}