de063290f4
Веха 2 (2D-ЧЕРТЁЖ), инкремент 1. Новый DrawingService (API7, namespace
Kompas.Mcp.Core.Drawings): doc2d.ViewsAndLayersManager.Views.AddStandartViews(
path, "#Спереди", {1,3,5}=front/top/left, x, y, scale, 20, 20). Новый DrawingTools
(инструмент drawing_create_standard_views), регистрация в DI.
Подтверждено спайком: object[]→SAFEARRAY VT_I4 ОК, 3 вида создаются (Count 1→4).
Ревью Codex спека учтено: File.Exists+Length>0; created==3 строго; гарантия
непустоты (сумма ObjectCount>0); опц. x,y (повторный вызов не стопкой); record
DrawingViewsResult{Created,Total}. Точечный release RCW отклонён (v2-2).
Валидация — чистый DrawingValidation (расширение .m3d/.a3d, scale>0).
Тесты: +16 unit (DrawingValidation), +3 integration (DrawingTests: создание 3 видов,
отклонение не-чертежа, несуществующий файл). Итого 114 unit + 61 integration = 175.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
92 lines
4.9 KiB
C#
92 lines
4.9 KiB
C#
using System.Runtime.Versioning;
|
|
using Kompas.Mcp.Core.Threading;
|
|
using Kompas6Constants;
|
|
using KompasAPI7;
|
|
|
|
namespace Kompas.Mcp.Core.Drawings;
|
|
|
|
/// <summary>
|
|
/// Операции с 2D-чертежами через API7 (<c>IViews</c>). Все вызовы — на STA-потоке
|
|
/// <see cref="KompasDispatcher"/>.
|
|
/// </summary>
|
|
[SupportedOSPlatform("windows")]
|
|
public sealed class DrawingService
|
|
{
|
|
private readonly KompasSession _session;
|
|
private readonly KompasDispatcher _dispatcher;
|
|
|
|
public DrawingService(KompasSession session, KompasDispatcher dispatcher)
|
|
{
|
|
_session = session ?? throw new ArgumentNullException(nameof(session));
|
|
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Создать на активном чертеже стандартные ассоциативные виды (спереди/сверху/слева) сохранённой
|
|
/// 3D-модели <paramref name="partFilePath"/> (.m3d/.a3d). Главный вид привязывается к точке
|
|
/// (<paramref name="x"/>,<paramref name="y"/>) листа, мм; масштаб — <paramref name="scale"/>.
|
|
/// </summary>
|
|
public Task<DrawingViewsResult> CreateStandardViewsAsync(string partFilePath, double scale,
|
|
double x, double y, CancellationToken ct = default)
|
|
=> _dispatcher.InvokeAsync(() => CreateStandardViewsCore(partFilePath, scale, x, y), ct);
|
|
|
|
// Стандартная тройка проекций: ksPtFront=1, ksPtUp=3, ksPtLeft=5 (ksRelativeProjectionTypeEnum).
|
|
private static readonly object[] StandardProjections = { 1, 3, 5 };
|
|
private const int ExpectedCreated = 3;
|
|
private const double GapMm = 20; // зазоры между видами (DX=DY)
|
|
|
|
private DrawingViewsResult CreateStandardViewsCore(string partFilePath, double scale, double x, double y)
|
|
{
|
|
var path = DrawingValidation.NormalizeModelPath(partFilePath);
|
|
DrawingValidation.RequireScale(scale);
|
|
if (!double.IsFinite(x) || !double.IsFinite(y))
|
|
throw new ArgumentException("Координаты привязки вида должны быть конечными числами.");
|
|
if (!File.Exists(path))
|
|
throw new FileNotFoundException($"Файл модели не найден: {path}", path);
|
|
if (new FileInfo(path).Length == 0)
|
|
throw new InvalidOperationException($"Файл модели пуст (не сохранён?): {path}");
|
|
|
|
var views = RequireActiveDrawingViews();
|
|
var before = views.Count;
|
|
|
|
if (!views.AddStandartViews(path, "#Спереди", StandardProjections, x, y, scale, GapMm, GapMm))
|
|
throw new InvalidOperationException(
|
|
$"AddStandartViews вернул FALSE — виды не созданы (файл модели или проекция недоступны): {path}");
|
|
|
|
var created = views.Count - before;
|
|
if (created != ExpectedCreated)
|
|
throw new InvalidOperationException(
|
|
$"Ожидалось {ExpectedCreated} вида, создано {created} (было {before}, стало {views.Count}).");
|
|
|
|
// Гарантия непустоты: у спроецированных видов должна быть геометрия (иначе модель не
|
|
// спроецировалась — пустые виды). Системный вид #0 имеет ObjectCount 0, поэтому суммируем.
|
|
if (TotalObjectCount(views) <= 0)
|
|
throw new InvalidOperationException(
|
|
"Стандартные виды созданы, но пусты (модель не спроецировалась). Проверьте файл модели.");
|
|
|
|
return new DrawingViewsResult { Created = created, Total = views.Count };
|
|
}
|
|
|
|
private IViews RequireActiveDrawingViews()
|
|
{
|
|
var doc = _session.Application.ActiveDocument
|
|
?? throw new InvalidOperationException("Нет активного документа. Создайте чертёж: document_create drawing.");
|
|
if (doc.DocumentType != DocumentTypeEnum.ksDocumentDrawing)
|
|
throw new InvalidOperationException(
|
|
$"Активный документ не является чертежом (тип: {doc.DocumentType}). Создайте чертёж: document_create drawing.");
|
|
if (doc is not IKompasDocument2D doc2d)
|
|
throw new InvalidOperationException("Активный документ не приводится к IKompasDocument2D.");
|
|
return doc2d.ViewsAndLayersManager?.Views
|
|
?? throw new InvalidOperationException("Не удалось получить коллекцию видов чертежа.");
|
|
}
|
|
|
|
private static int TotalObjectCount(IViews views)
|
|
{
|
|
int sum = 0;
|
|
foreach (var o in views)
|
|
if (o is IView v)
|
|
sum += v.ObjectCount;
|
|
return sum;
|
|
}
|
|
}
|