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); }