docs: спек пакета C — массивы и зеркало (linear/circular pattern, mirror)
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# Дизайн: пакет C — массивы и зеркало (linear/circular pattern, mirror)
|
||||
|
||||
**Дата:** 2026-05-26
|
||||
**Статус:** на ревью (автономный режим; ревьюер — Codex)
|
||||
|
||||
## Цель
|
||||
|
||||
Добавить класс операций «размножение»: **линейный массив** (по сетке вдоль координатной
|
||||
оси), **круговой массив** (вокруг координатной оси) и **зеркальное отражение** (операций
|
||||
относительно координатной плоскости; и тела целиком). Самый ценный недостающий класс
|
||||
(NEXT-SESSION приоритет 1). Реализуется тремя инкрементами, по одной операции за раз.
|
||||
|
||||
Все три операции в API5 строятся одним паттерном: `NewEntity(o3d_*)` → `GetDefinition()` →
|
||||
задать **источник** (коллекция копируемых операций / тел) + **направление** (ось/плоскость) →
|
||||
`Create()` → регистрация в `_features`. Источник копирования — `ksEntity` ранее созданных
|
||||
операций, на которые ссылаемся по их id из реестра `_features` (те же id, что возвращают
|
||||
extrude/revolve/fillet/…).
|
||||
|
||||
## Сигнатуры COM (рефлексия interop + справка SDK)
|
||||
|
||||
```
|
||||
Obj3dType.o3d_meshCopy = 35 // массив по сетке (линейный)
|
||||
Obj3dType.o3d_circularCopy = 36 // круговой массив
|
||||
Obj3dType.o3d_mirrorOperation = 48 // зеркальная копия выбранных операций
|
||||
Obj3dType.o3d_mirrorAllOperation = 49 // зеркально отразить тело(а) целиком
|
||||
Obj3dType.o3d_axisOX/OY/OZ = 71/72/73 // координатные оси (GetDefaultEntity)
|
||||
Obj3dType.o3d_planeXOY/XOZ/YOZ = 1/2/3 // координатные плоскости
|
||||
|
||||
ksMeshCopyDefinition: // линейный (по сетке)
|
||||
bool SetAxis1(object axisOrEdge) // ось 1 (коорд. ось или прямое ребро)
|
||||
bool SetAxis2(object axisOrEdge) // ось 2 (для 2D-сетки)
|
||||
bool SetCopyParamAlongAxis(bool firstAxis, double angle, int count, double step, bool factor)
|
||||
int count1/count2; double step1/step2; double angle1/angle2; bool factor1/factor2
|
||||
bool geomArray; bool insideFlag
|
||||
object OperationArray() // ksEntityCollection источников
|
||||
|
||||
ksCircularCopyDefinition: // круговой
|
||||
bool SetAxis(object axis) // ось вращения
|
||||
bool SetCopyParamAlongDir(int count, double step, bool factor, bool dir)
|
||||
int count1/count2; double step1/step2; bool factor1/factor2; bool geomArray; bool inverce
|
||||
object GetOperationArray() // ksEntityCollection источников
|
||||
|
||||
ksMirrorCopyDefinition: // зеркало выбранных операций
|
||||
bool SetPlane(object plane) // плоскость симметрии (коорд. или плоская грань)
|
||||
object GetOperationArray() // ksEntityCollection источников
|
||||
|
||||
ksMirrorCopyAllDefinition: // зеркало тела(а) целиком
|
||||
bool SetPlane(object plane)
|
||||
object ChooseBodies() // ksChooseBodies (необяз.: все тела по умолчанию)
|
||||
```
|
||||
|
||||
**Семантика (из справки, проверить эмпирически):**
|
||||
- `count` — количество **всех** экземпляров, **включая исходный** (count=3 → +2 копии).
|
||||
- линейный `step` — мм между **соседними** при `factor=false`; круговой `step` — **угол в
|
||||
градусах** между соседними при `factor=false`.
|
||||
- `count` включает оригинал → требуем `count >= 2`.
|
||||
|
||||
## Новые MCP-инструменты (группа Feature)
|
||||
|
||||
| Инструмент | Параметры | Поведение |
|
||||
|---|---|---|
|
||||
| `linear_pattern` | `featureIds: int[]`, `axis: "X"\|"Y"\|"Z"`, `count: int`, `step: double`, `geometric=false` | Линейный массив: размножить операции `featureIds` вдоль координатной оси `axis`, `count` экз. всего (вкл. исходную, ≥2), шаг `step` мм между соседними. `geometric` → быстрое геом. копирование. Возвращает id. |
|
||||
| `circular_pattern` | `featureIds: int[]`, `axis: "X"\|"Y"\|"Z"`, `count: int`, `step=` (угол, °), `reverse=false`, `geometric=false` | Круговой массив вокруг координатной оси `axis`, `count` экз. всего (≥2), угловой шаг `step` ° между соседними (по умолчанию 360/count для равномерного — см. ниже). `reverse` — обратное направление. Возвращает id. |
|
||||
| `mirror_operation` | `featureIds: int[]`, `plane: "XOY"\|"XOZ"\|"YOZ"` | Зеркальная копия операций `featureIds` относительно координатной плоскости `plane`. Возвращает id. |
|
||||
| `mirror_body` | `plane: "XOY"\|"XOZ"\|"YOZ"` | Зеркально отразить всё тело относительно плоскости (получить симметричную деталь). Возвращает id. |
|
||||
|
||||
**Решения (YAGNI / инкрементальность):**
|
||||
- **Ось/плоскость — только координатные** (X/Y/Z, XOY/XOZ/YOZ) на первом этапе: детерминированно,
|
||||
легко тестируется, покрывает большинство случаев. Выбор по ребру/грани — отдельная задача позже.
|
||||
- **Линейный массив — 1D** (одна ось): `SetAxis1` + `SetCopyParamAlongAxis(true,…)`, `count2=1`
|
||||
(второе направление выключено). 2D-сетку добавим позже при необходимости.
|
||||
- `factor=false` (шаг между соседними — интуитивнее для пользователя).
|
||||
- Круговой `step`: задаём явный угловой шаг (°). Дефолт инструмента не подставляем — пусть
|
||||
агент передаёт (например 90 для 4 шт). `factor=false`.
|
||||
- `mirror_body` использует `ksMirrorCopyAllDefinition` без `ChooseBodies` (по умолчанию все тела).
|
||||
- `geomArray` дефолт `false` (полное копирование операции — надёжнее для несложных тел).
|
||||
|
||||
## Реализация
|
||||
|
||||
- **Enum** `CoordinateAxis { X, Y, Z }` + `CoordinateAxes.ToObj3dType/Parse` — новый файл
|
||||
`Core/Modeling/CoordinateAxis.cs` (по аналогии с `BasePlane.cs`).
|
||||
- **Методы** в `PartModeler.Features.cs`:
|
||||
- `LinearPatternAsync(IReadOnlyList<int> featureIds, CoordinateAxis axis, int count, double step, bool geometric, CancellationToken)`
|
||||
- `CircularPatternAsync(IReadOnlyList<int> featureIds, CoordinateAxis axis, int count, double step, bool reverse, bool geometric, CancellationToken)`
|
||||
- `MirrorOperationAsync(IReadOnlyList<int> featureIds, BasePlane plane, CancellationToken)`
|
||||
- `MirrorBodyAsync(BasePlane plane, CancellationToken)`
|
||||
- **Хелпер** `RequireFeature(int id)` в `PartModeler.cs` (по аналогии с `RequireSketch`):
|
||||
`_features.TryGetValue` → `ksEntity` или `KeyNotFoundException`.
|
||||
- **Валидация** (чистая, `SketchGeometry`): `RequireMin(int value, int min, string)` для `count>=2`;
|
||||
`RequirePositive(step, …)` (ловит NaN). Списки `featureIds`: not null, `Distinct`, не пуст,
|
||||
все id существуют (проверка до мутации модели — собрать `ksEntity` заранее).
|
||||
- **Инструменты** в `FeatureTools.cs`: `linear_pattern`, `circular_pattern`, `mirror_operation`,
|
||||
`mirror_body`; `axis`/`plane` парсятся из строки (`CoordinateAxes.Parse`/`BasePlanes.Parse`).
|
||||
- Имена методов коллекции в interop различаются: MeshCopy — `OperationArray()`; Circular и
|
||||
Mirror — `GetOperationArray()` (по рефлексии). Все → `as ksEntityCollection ?? throw`.
|
||||
- Транзитные RCW не освобождаем точечно — консистентно (долг v2-2).
|
||||
- `Create()==FALSE` → `InvalidOperationException` с подсказкой (несовместимая геометрия / шаг
|
||||
ведёт к самопересечению / неверная ось).
|
||||
|
||||
## Тестирование (Integration, `FeatureOpsTests`)
|
||||
|
||||
Паттерн: `CreateAsync(Part)` → построить базовую операцию (запомнить её id) → массив/зеркало →
|
||||
`RebuildAsync` → проверить объём (`GetPartInfoAsync`) и габарит (`GetBoundingBoxAsync`); `finally CloseAsync`.
|
||||
|
||||
1. **linear_pattern**: цилиндр R5×H10 в центре (V₁≈785 мм³) → `linear_pattern([cyl], X, 3, 30)` →
|
||||
3 непересекающихся цилиндра → V≈3·785=2356 (`InRange ±5%`); габарит X≈70 (от −5 до 65).
|
||||
2. **circular_pattern**: цилиндр R3×H10 со смещением (центр (20,0)) → `circular_pattern([cyl], Z, 4, 90)` →
|
||||
4 цилиндра по кругу R20 → V≈4·283=1131 (`±5%`); габарит X≈Y≈46 (от −23 до 23).
|
||||
3. **mirror_operation**: цилиндр R3×H10 центр (20,0) → `mirror_operation([cyl], YOZ)` → копия в
|
||||
(−20,0) → V≈2·283=565 (`±5%`); габарит X≈46.
|
||||
4. **mirror_body**: то же тело → `mirror_body(YOZ)` → симметричная пара, габарит X≈46, V≈2·283.
|
||||
|
||||
Ожидаемые объёмы считаем аналитически, сверяем `InRange ±5%`. Если `count` НЕ включает
|
||||
оригинал или направление шага иное — корректируем по факту, сохранив суть проверки.
|
||||
Reference in New Issue
Block a user