diff --git a/README.md b/README.md index d28847b..3d1d6d5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ STEP/STL, осмотр и измерение модели — плюс два н ## Что нужно до установки - **Windows x64** — сервер является COM-клиентом КОМПАС и работает только на той же машине; -- **установленный КОМПАС-3D** (проверено на v24 Home) **с компонентом SDK**. Interop-сборки +- **установленный КОМПАС-3D** (проверено на v25 Home, v24 Home тоже поддерживается) **с компонентом SDK**. Interop-сборки принадлежат АСКОН, в поставку не входят и берутся из установки (`SDK\Samples\CSharp.zip`, каталог `Common`); при первом запуске они распаковываются в `%LOCALAPPDATA%\kompas-mcp\interop\<ключ>`. Если SDK не установлен, укажите каталог diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index e05b765..25214b5 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "kompas", "displayName": "КОМПАС-3D", - "version": "0.2.1", + "version": "0.3.0", "description": "Управление КОМПАС-3D из Claude Code: детали, сборки, чертежи, STEP — через MCP-сервер", "author": { "name": "Shahovalov Mikhail", diff --git a/plugin/README.md b/plugin/README.md index 4f17192..1f66e00 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -6,7 +6,7 @@ ## Предпосылки - Windows x64; -- установленный КОМПАС-3D (проверено на v24 Home) — сервер является COM-клиентом, а не автономным +- установленный КОМПАС-3D (проверено на v25 Home, v24 Home тоже поддерживается) — сервер является COM-клиентом, а не автономным CAD-движком, и работает только на той же машине. Запускать заранее не нужно: сервер присоединится к открытому окну, а если его нет — запустит экземпляр сам; - КОМПАС установлен **вместе с компонентом SDK**: interop-сборки принадлежат АСКОН, в поставку diff --git a/plugin/bin/Kompas.Mcp.Core.dll b/plugin/bin/Kompas.Mcp.Core.dll index 7ed0e7c..e49ef48 100644 Binary files a/plugin/bin/Kompas.Mcp.Core.dll and b/plugin/bin/Kompas.Mcp.Core.dll differ diff --git a/plugin/bin/Kompas.Mcp.Core.pdb b/plugin/bin/Kompas.Mcp.Core.pdb index 8631906..1cbe18b 100644 Binary files a/plugin/bin/Kompas.Mcp.Core.pdb and b/plugin/bin/Kompas.Mcp.Core.pdb differ diff --git a/plugin/bin/kompas-mcp.deps.json b/plugin/bin/kompas-mcp.deps.json index cdc5943..52f9b9c 100644 --- a/plugin/bin/kompas-mcp.deps.json +++ b/plugin/bin/kompas-mcp.deps.json @@ -7,9 +7,9 @@ "targets": { ".NETCoreApp,Version=v8.0": {}, ".NETCoreApp,Version=v8.0/win-x64": { - "kompas-mcp/0.2.1": { + "kompas-mcp/0.3.0": { "dependencies": { - "Kompas.Mcp.Core": "0.2.1", + "Kompas.Mcp.Core": "0.3.0", "Microsoft.Extensions.Hosting": "8.0.1", "ModelContextProtocol": "2.0.0" }, @@ -483,21 +483,21 @@ } } }, - "Kompas.Mcp.Core/0.2.1": { + "Kompas.Mcp.Core/0.3.0": { "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "10.0.10" }, "runtime": { "Kompas.Mcp.Core.dll": { - "assemblyVersion": "0.2.1", - "fileVersion": "0.2.1.0" + "assemblyVersion": "0.3.0", + "fileVersion": "0.3.0.0" } } } } }, "libraries": { - "kompas-mcp/0.2.1": { + "kompas-mcp/0.3.0": { "type": "project", "serviceable": false, "sha512": "" @@ -761,7 +761,7 @@ "path": "system.text.json/10.0.10", "hashPath": "system.text.json.10.0.10.nupkg.sha512" }, - "Kompas.Mcp.Core/0.2.1": { + "Kompas.Mcp.Core/0.3.0": { "type": "project", "serviceable": false, "sha512": "" diff --git a/plugin/bin/kompas-mcp.dll b/plugin/bin/kompas-mcp.dll index 045c690..54159d9 100644 Binary files a/plugin/bin/kompas-mcp.dll and b/plugin/bin/kompas-mcp.dll differ diff --git a/plugin/bin/kompas-mcp.exe b/plugin/bin/kompas-mcp.exe index 3ed5f32..54b71cb 100755 Binary files a/plugin/bin/kompas-mcp.exe and b/plugin/bin/kompas-mcp.exe differ diff --git a/plugin/bin/kompas-mcp.pdb b/plugin/bin/kompas-mcp.pdb index 5773db7..3fea11f 100644 Binary files a/plugin/bin/kompas-mcp.pdb and b/plugin/bin/kompas-mcp.pdb differ diff --git a/plugin/skills/kompas-3d/SKILL.md b/plugin/skills/kompas-3d/SKILL.md index 1e577c9..e78ff42 100644 --- a/plugin/skills/kompas-3d/SKILL.md +++ b/plugin/skills/kompas-3d/SKILL.md @@ -23,6 +23,26 @@ MCP-сервер даёт **общие** операции КОМПАС (эски Любая задача «сделать что-то В КОМПАС» через MCP: создать/править деталь, эскизы и операции, импорт/экспорт обменных форматов, разбор сборки, измерения, снимки. +## Указатель: тип задачи → файл + +Этот файл — ядро: применяется в любой задаче. Развёрнутый playbook под конкретный тип работы вынесен +в `reference/*.md` — читай Read'ом ровно тот файл, что подошёл, остальные не трогай. + +| Задача | Файл | +|---|---| +| Переменные, параметрическая модель | `reference/parametrization.md` | +| Надпись, логотип, SVG, рельеф, выбор маршрута для внешнего контура | `reference/text-and-relief.md` | +| Переиспользуемый чертёжный блок (*.frw) | `reference/fragments.md` | +| Сборка с нуля (вставка, сопряжения, фиксация) | `reference/assembly-playbook.md` | +| Чертёж по ГОСТ, виды, разрез, размеры | `reference/drawing-playbook.md` | +| Разбор ЧУЖОЙ модели/сборки (импорт, STEP) | `reference/import-playbook.md` | +| Исполнения (типоразмеры, зеркала в одном файле) | `reference/embodiments-playbook.md` | + +Ни один файл не подошёл — вопрос, скорее всего, про механику КОНКРЕТНОГО инструмента, а не про +порядок работы: туда — `help(topic="…")`/`help(query="…")` (см. ниже), а не в `reference/`. +Не нашёлся и там — `Grep` по `reference/**` словами задачи: это и есть весь механизм поиска, ничего +специального заводить не пришлось. + ## Два правила прежде всего **1. «Зрение» — структурное, не по картинке.** «Увидеть» деталь = **`describe_model`** — паспорт @@ -40,7 +60,7 @@ variables | errors | components` (`sections` сужает и ответ, и об скажет «Документ ТРЕБУЕТ ПЕРЕСТРОЕНИЯ» (`needsRebuild: true`); увидел — `rebuild` и повтори осмотр, выводов по непересчитанной модели не делай. -**2. Ответ мутирующей операции — это ТРИ проверки, читай все.** Любая операция может «пройти» +**2. Ответ мутирующей операции — это НЕСКОЛЬКО проверок, читай все.** Любая операция может «пройти» (`Create()==true`), оставив деталь в ошибке, поэтому каждая мутирующая операция сама дописывает к ответу итог проверки построения и сводку состояния «Тел: N, объём V мм³»: @@ -52,9 +72,27 @@ variables | errors | components` (`sections` сужает и ответ, и об - **«Тел: 2»** там, где ждёшь одно тело, — деталь распалась: касание объединением не считается (см. «Надписи»). Смотри на число ТЕЛ, а не граней: объединение сливает компланарные грани, и счётчик граней законно остаётся прежним. + **Но обратное неверно: «Тел: 1» распад не опровергает.** Счётчик в сводке ОПАЗДЫВАЕТ. Замер + (журнал операций кейса с хвостом по сечениям): `loft` построил второе тело и отчитался + «Тел: 1», следом `mirror` — снова «Тел: 1», и только третья операция, `chamfer_edge`, напечатала + «Тел: 2», хотя между ними ничего не создавалось. Три операции подряд шли по детали, объём + которой считал пересечение дважды. Поэтому «Тел: 2» — тревога, которой верят сразу, а «Тел: 1» — + не доказательство: там, где важно именно СРАСТАНИЕ (loft/sweep на грань, надпись, примитив + впритык), проверяй `describe_model(sections="bodies")` — у каждого тела там свой габарит — и сверяй + объём с прикидкой. - **Объём сверяй с прикидкой** «площадь контура × высота». Так ловятся молча не применившаяся тонкая стенка (прирост как у сплошного сечения), вычитание мимо цели и операция «не туда» — габарит и снимок этого не покажут. +- **«⚠ Сверка с запросом: …»** — четвёртая, отдельная от первых трёх строка: сервер сравнил + результат с ВХОДНЫМИ параметрами вызова. Сейчас это положение материала у `extrude(mode=boss)` + и покрытие сечений у `loft(mode=boss)` (на базовых и смещённых плоскостях), убыль объёма у + `extrude(mode=cut)`, а у `primitive` — объём против номинала по его же размерам. Сработала — + построено не то, о чём просили, и в тексте сказано что именно. **Молчание успех НЕ доказывает**: + сверка односторонняя и намеренно молчит там, где улика неоднозначна (прирост меньше + запрошенного, материал целиком внутри габарита, эскиз выдавливания на грани, сечения лофта на + наклонной грани или с одинаковыми именами). Размер-выражение сверке не мешает: и у `primitive`, + и у `extrude` величина проверяется по уже вычисленному значению. Механика и границы — + `help(topic="build-verification")`. Отдельный `describe_model(sections=errors,bodies,mass)` после каждого шага не нужен. Он возвращается, когда авто-проверку выключили (`set_auto_validate(false)` на тяжёлой модели, где @@ -76,58 +114,12 @@ variables | errors | components` (`sections` сужает и ответ, и об намеренно не повторяется. Справочник описывает ЭТОТ сервер; справка по COM API КОМПАС — отдельная история и в работе через MCP не нужна. -## Инструменты: индекс групп +## Инструменты: где искать каталог -Актуальный перечень с описаниями параметров отдаёт сам MCP-сервер — здесь только карта: - -- *Система/документы:* `kompas_connect` (shared|private|attach), `kompas_set_visible`, - `kompas_status`, `set_auto_validate`, `set_operation_log`; `document_create|open|save|close`, - `document_active` (тип, путь, есть ли несохранённые изменения, «только чтение», материал детали), - `document_list` (все открытые документы — им проверяют, что имя файла занято другим документом - и что несохранённого не осталось), `set_part_info`, `set_part_material`. -- *Эскиз:* `sketch_create` (основание: `plane`+`offset` | `faceIndex` | точка; геометрия — списком - `entities` **или** файлом `entitiesFile`), `sketch_add`, `sketch_close`; `measure_text` — замер - надписи без построения; `fragment_create` — тот же список примитивов, но в самостоятельный - файл-фрагмент (*.frw); `fragment_place` — положить готовый фрагмент в чертёж. -- *Формообразующие:* `primitive`, `extrude`, `revolve`, `fillet_edge`, `chamfer_edge`, `shell`, - `rib`, `sweep`, `loft`, `draft`, `hole`, `pattern`, `mirror`, `feature_delete`, `rebuild`. -- *Прямое редактирование (в т.ч. импортированная B-rep):* `move_face`, `split_solid_by_plane`, - `move_body`, `boolean_union`. -- *Переменные:* `set_variable`, `list_parameters`, `link_parameter`, `delete_variable` - (сами переменные показывает `describe_model(sections=variables)`). -- *Обмен:* `import_model` (3D: step, iges, sat, xt, stl, c3d, jt, obj + родные форматы чужих САПР), - `import_drawing` (плоский .dxf/.dwg), `export_model` (`format=auto` по расширению: `.step` — - точная геометрия в другую САПР, `.stl` — сетка в слайсер). -- *Исполнения:* `embodiment` (`set|add|delete|rename`; `kind=embodiment|mirror|variant`) — несколько - геометрий одной модели в одном файле; список с габаритом и массой каждого — - `describe_model(sections=embodiments)`. -- *Сборка:* `assembly_add_component` (положение + поворот `rx/ry/rz`, `embodiment` — какое исполнение - источника вставлять), `assembly_add_mate` (стороной может быть грань ИЛИ вспомогательный объект - `object1/object2`: плоскости и оси СК компонента, а без `componentIndex` — самой сборки) - (семь типов, `orientation`), `assembly_fix_component`, `assembly_delete`, - `assembly_check_interference` (не налезли ли детали друг на друга — больше это не видно нигде), - `assembly_transform_point` (`to=world|local` — точка между ЛСК компонента и СК сборки). -- *2D-чертёж:* `drawing_create_standard_views` (`views[]`, `mainOrientation`, `hiddenLines`), - `drawing_add_view` / `drawing_move_view` / `drawing_delete_view` / `drawing_get_view_info` - (компоновка листа и паспорт вида), `drawing_add_section_view` (РАЗРЕЗ или СЕЧЕНИЕ: линия разреза - на базовом виде и производный вид по ней — одним вызовом), `drawing_add_sheet`, - `drawing_set_sheet_format`, - `drawing_fill_title_block`, - `drawing_add_linear|diametral|radial|angular_dimension` (у всех — `tolerance`/`prefix`/`suffix`/ - `textOverride`; у Ø и R — ещё `objectKind`/`objectIndex`, прямой адрес окружности или дуги), - `drawing_add_axis`, `drawing_add_rough|text|leader`, - `drawing_set_unspecified_rough`, `drawing_set_technical_requirements`, `drawing_delete_object`, - `drawing_move_object` (сдвинуть поставленное), `drawing_project_point` (точка модели → координаты - вида), `drawing_export_image` (посмотреть - глазами). Обратное чтение — `drawing_get_title_block` и `drawing_get_technical_requirements`: - технические требования ПЕРЕЗАПИСЫВАЮТСЯ целиком, поэтому правку одного пункта начинают с чтения - текущего текста. Методика — §«Чертёж». -- *Осмотр:* `describe_model` (разделы `box|mass|bodies|topology|tree|variables|errors|components|mates` — - габарит, МЦХ, тела, топология, дерево построения, переменные, операции в ошибке, состав сборки, - сопряжения), `measure`, `model_snapshot`; адресация объектов — `list_faces` / `list_edges`. - Для 2D-документов — свой: `list_drawing_objects` (что лежит в чертеже/фрагменте). - -Три сквозных свойства контракта: **размерный параметр принимает число ИЛИ выражение** (имя +Полный перечень 86 инструментов с параметрами — README плагина §«Инструменты» (единый источник +правды, держать вторую копию здесь не нужно) или `tools/list` самого MCP-сервера. Что известно про +КОНКРЕТНЫЙ инструмент — `help(tool="extrude")`. Три сквозных свойства контракта, которые полезно +держать в голове, а не искать заново на каждом инструменте: **размерный параметр принимает число ИЛИ выражение** (имя переменной/формулу — параметр сразу становится ведомым); **объект выбирается индексом** (надёжно) или точкой (запасной путь — если выбирал точкой, ответ подскажет, что выбралось); **списковые параметры** (`entities[]`, `edgeIndices[]`, `points[]`, `faceIndices[]`, `variables[]`, `links[]`) — @@ -138,6 +130,14 @@ variables | errors | components` (`sections` сужает и ответ, и об остаётся — повторяй вызов с оставшимися, а не с начала. Если инструмента под задачу нет — собирай результат из имеющихся общих операций. +**Оговорка для больших групп рёбер в `fillet_edge`/`chamfer_edge`.** «Один вызов на весь пакет» +экономит вызовы, но у катета/радиуса есть рабочий диапазон, заданный ВСЕЙ группой рёбер разом, а +не отдельным ребром (измерено: один и тот же контур строится с меньшим катетом и отказывает с +большим). Параметрическая модель может пройти это на одном значении переменной и отказать на +другом — большая группа сама по себе фактор хрупкости при последующей правке размеров. Если пакет +на 15+ рёбер отказывает без диагноза по длине, разбейте его на 2–3 группы — сервер сам подскажет +это в тексте отказа (`help(topic="edge-groups")`). + ## Два пути формообразования — выбирай по форме, а не по привычке «Эскиз → выдавливание» — не единственный способ. `primitive` строит тело сразу по размерам, и это @@ -148,7 +148,9 @@ variables | errors | components` (`sections` сужает и ответ, и об | Плита, брусок, бобышка, штифт, цилиндрическая стойка | `primitive(kind=block\|cylinder)` — один вызов вместо «эскиз + выдавливание» | | Прямоугольный карман, паз, срез угла | `primitive(..., result=subtract)` — вычитание тела вместо `extrude(mode=cut)` по эскизу | | Скруглённые углы призмы | `primitive` + `fillet_edge` по рёбрам (скругление после, а не дуги в эскизе) | -| Текст, кривые, произвольный контур, переменное сечение | ТОЛЬКО эскиз: `sketch_create(entities=[…])` + `extrude`/`revolve`/`loft`/`sweep` | +| Текст, кривые, произвольный контур | ТОЛЬКО эскиз: `sketch_create(entities=[…])` + `extrude`/`revolve` | +| Переменное сечение: хвост, гребень, переход круга в квадрат | `loft` по сечениям на смещённых плоскостях (`mode=boss` приклеивает к телу) | +| Труба, поручень, кант по кривой | `sweep(profileSketchId, pathSketchId)` — профиль и траектория на разных плоскостях | | Тонкая стенка, ободок по контуру | эскиз + `extrude(thinThickness, thinSide)` | Их **сочетают в одной детали**: корпусные объёмы — примитивами, сложные контуры — эскизами. @@ -170,8 +172,26 @@ variables | errors | components` (`sections` сужает и ответ, и об координат — одна операция на все; точки без ребра ошибка перечислит разом. - **Скругление не построится там, где ребро «съедено» соседним элементом** (угол плашки под каймой букв). Это нормальный ответ геометрии, а не ошибка вызова: проверь снимком, виден ли угол снаружи. +- **Фаска на почти касательном стыке может ДОСТРОИТЬ объём, а не срезать.** Замер: `chamfer_edge` + 0.3 по кромке, где поверхность лофта сходит в плоскость почти по касательной, — «построение + чистое», а объём ВЫРОС на 2.2 мм³. На выпуклом ребре фаска обязана объём УМЕНЬШАТЬ (на вогнутом + легитимно добавляет); сверяй ЗНАК приращения в сводке ответа со своим ожиданием — вырос без + причины, значит фаска выродилась в достройку: убери её `feature_delete`, пологому стыку она + и не нужна. - **`result=new` даёт ОТДЕЛЬНОЕ тело**, `subtract` мимо цели сервер ловит сам — и то и другое видно по сводке «Тел/объём» в ответе; при распаде зови `boolean_union`. +- **Сужающуюся или изогнутую форму не набирай лесенкой из примитивов.** `primitive` не умеет + поворота, и «хвост» из десятка блоков со ступеньками — это обход отсутствующего инструмента, + а не решение: он и печатается хуже, и правится только целиком. Бери `loft` по 4–6 сечениям на + смещённых плоскостях — параметричность не теряется, `offset` плоскости принимает выражение. + **`loft` может отказать молча, и отказ зависит от ПРОЛЁТА, а не от одного сечения** — поиск + виновника перебором пар не работает, ломается СОЧЕТАНИЕ «сечение-выброс + длина набора»; механика + и порядок поиска — `help(topic="loft-and-sweep")`. +- **`loft(mode=boss)` прирастает перекрытием, а не касанием.** Первое сечение ставь на плоскость + ВНУТРИ уже построенного тела (смещение на 1–2 мм вглубь), а не на его грань: касание объединением + не считается — на грани получишь ВТОРОЕ тело вместо приросшего. **Проверять это сводкой «Тел: 1» + НЕЛЬЗЯ** — именно здесь она и соврала (см. правило 2): считай тела + `describe_model(sections="bodies")`, там у каждого свой габарит, и сверь объём с прикидкой. ## Базовый цикл (эскиз → операция → осмотр) @@ -203,29 +223,16 @@ variables | errors | components` (`sections` сужает и ответ, и об пересборки. Набор переменных — это план построения числами и паспорт, по которому человек поймёт модель, не разбирая дерево. -- **Заводи переменной то, что было РЕШЕНИЕМ**: габариты из ТЗ, толщины, зазоры, отступы, кегли, - радиусы. Выводимое задавай формулой (`plate_L = badge_L - 2*edge_gap`) — правка ведущего размера - пересчитает зависимые сама. Позиции, подобранные замером, оставляй числами: связь — обещание, - что правка переменной даст осмысленный результат, а на позиционных числах она чаще ломает деталь. -- **Размер передавай выражением прямо в операцию** (`extrude(depth="badge_T")`) — параметр сразу - становится ведомым, внутреннее имя параметра («Расстояние 1») знать не нужно. Переменная должна - существовать ДО операции — потому паспорт и заводят первым. Задним числом и для параметров, - которых нет среди аргументов инструмента (углы уклона, второе направление), — `link_parameter`. -- **Промахи сервер ловит сам:** ссылка на несуществующее имя — отказ с перечнем виновных (и в - выражениях операций, и в `set_variable`; регистр значим), функции в формулах — предупреждение - (тригонометрия в радианах, разделитель аргументов «;», непонятая запись = молчаливый 0). - Твоя часть — **сверить вычисленные значения из ответа со своей прикидкой**. -- **`note` обязателен** и пишется по-русски, со смыслом и единицей; `(ТЗ)` метит числа, - зафиксированные пользователем. **Что замерил — верни в переменную** (фактический кегль, прирост - каймы), иначе паспорт разойдётся с моделью и станет дезинформацией. Переосмысливать можно всё, - кроме помеченного `(ТЗ)`. -- **После связывания правка паспорта перестраивает деталь** (проверено: `set_variable(badge_T=4)` → - габарит по Z 3.00 → 4.00, объём 5154.5 → 5861.5 мм³). Это и есть параметрическая модель, - а не комментарий к ней. **До связывания — не перестраивает**, и `set_variable` говорит об этом - прямо: «⚠ НИЧЕГО НЕ ВЕДЁТ». Такой ответ — не успех: число поменялось, деталь нет. -- **Сверка и уборка в конце:** `describe_model(sections=variables)` помечает «⚠ ничего не ведёт» — это либо забытая - связь, либо мусор; разбери оба. `delete_variable(unused=true)` — только ПОСЛЕ `link_parameter` - на всё, что должно вести геометрию: вычистка не отличает мусор от несвязанного размера из ТЗ. +- **Заводи переменной то, что было РЕШЕНИЕМ** (габариты из ТЗ, толщины, зазоры), позиции, подобранные + замером, оставляй числами; **размер передавай выражением прямо в операцию** + (`extrude(depth="badge_T")`) — переменная должна существовать ДО операции. +- **После связывания правка паспорта перестраивает деталь; до связывания — нет**, и `set_variable` + прямо говорит «⚠ НИЧЕГО НЕ ВЕДЁТ» в этом случае — не успех, хоть число и поменялось. +- **Сверка и уборка в конце:** `describe_model(sections=variables)` помечает «⚠ ничего не ведёт»; + `delete_variable(unused=true)` не трогает справочные (`information`) и внешние. + +Полная методика — обязательные поля (`note`), как сервер ловит промахи в формулах, справочные +переменные `(ТЗ)` и порядок уборки — [reference/parametrization.md](reference/parametrization.md). ## Надписи, логотипы и рельеф @@ -247,301 +254,54 @@ variables | errors | components` (`sections` сужает и ответ, и об - **Надпись и плашка обязаны ПЕРЕКРЫВАТЬСЯ** — по размерам чертежа блоки обычно только касаются, а касание объединением не считается. Приём: кайму/полосу плашки сделать выше самой плашки на 1–1.5 мм с той стороны, где стоит надпись. Распад видно по «Тел: 2» в ответе операции — не тяни - проверку до конца построения. + проверку до конца построения. Молчание сводки распад не опровергает (счётчик опаздывает, п. 2): + убедиться, что срослось, можно только `describe_model(sections="bodies")`. - **Рельеф = разница глубин от одной плоскости** (основание 2 мм + буквы 3 мм = выступ 1 мм); не строй буквы «на грани основания» — из одного эскиза на базовой плоскости получается и то и другое, и рельеф не зависит от порядка операций. +- **Надпись читается только с той стороны, куда смотрит нормаль плоскости эскиза** — XOY → +Z, + XOZ → +Y, **YOZ → −X**. Гравировка по эскизу на YOZ, положенная на грань с бОльшим X, выходит + ЗЕРКАЛЬНОЙ, и `angle` этого не исправляет: он вращает, а не отражает (замер — цифра на щеке + X=20.4 зеркальна при любом угле, тот же эскиз на X=0 встаёт верно). Выбирайте грань по нормали, + а поворотом задавайте только, куда смотрит верх глифа. Проверять — снимком С ТОЙ СТОРОНЫ, где + надпись будет видна: `model_snapshot(view=left)` для грани X=0, `right` для X=max. - **Шрифт должен быть установлен в системе** — неизвестное имя КОМПАС молча подменяет; сервер предупреждает об этом в ответе `sketch_create`/`measure_text` — не игнорируй. -**Логотип, которого нет в шрифте, приходит готовым вектором (SVG).** Переводи его в примитивы -эскиза сам — `line` и `arc3points`, а не ломаной: контур из сотен звеньев даёт деталь в сотни -граней, и фаска по такому низу не строится. Кубические Безье режь пополам, пока дуга по трём -точкам не ляжет в допуск. Четыре вещи, каждая из которых стоила захода: +**Логотип (SVG) и выбор маршрута для внешнего контура (вычисляемый / срисованный / CAD-файл) — +в [reference/text-and-relief.md](reference/text-and-relief.md).** Там же — точная подгонка (кегль +под заданную ширину, ступени `widthFactor`/`height`, приросты каймы на острых терминалах, разрядка +пробелами, кайма вокруг прямоугольной плашки) и отделка рельефа (скругления/фаски на сотнях рёбер). +Прочитай его, как только контур не сводится к простому вычисляемому эскизу. -- **Ось Y в SVG смотрит вниз** — без инверсии контур встаёт вверх ногами, и это видно только на снимке. -- **Дуги задавай тремя точками** (`arc3points`): концы заданы явно, соседние примитивы стыкуются - точно. Перед постройкой проверь цепочку — конец примитива против начала следующего, разрыв 0. -- **Чисти мелочь под масштаб детали — параметром `minSegment`.** SVG рисуют в своём габарите (сотня - условных единиц), и его скругления после масштабирования превращаются в дуги по 0.02–0.5 мм; - замыкающий `z` часто даёт ещё и сегмент нулевой длины. `chamfer_edge` по такому набору отвечает - «катет больше длины N из M рёбер» — и это правда. Передай `sketch_create(minSegment=катет)` (тот же - параметр есть у `sketch_update` и `fragment_create`), и та же фаска пройдёт с первой попытки; - отчёт о выброшенном читай, а не пролистывай — `help(topic="contour-cleanup")`. - -**Порядок для фаски по контуру логотипа — только такой, и он неочевиден:** - -1. **чистка** порогом ≈ будущего катета (`minSegment`); -2. **замер ЧИЩЕНОГО силуэта** — `sketch_measure_thickness(entitiesFile, minSegment=тот же порог)`; -3. **катет по узкому месту** чищеного силуэта: `c ≤ (узкое_место − 2·w)/2`, где `w` — ширина нити. - -Менять шаги местами нельзя: **чистка СУЖАЕТ узкое место**. Она заменяет скругление острым углом — -в выпуклом углу это добавляет материал (габарит подрастает), а в вогнутом срезает его, и именно -вогнутые углы образуют перемычки между штрихами. Замер обводки логотипа шириной 105 мм: -узкое место 1.98 → 1.82 мм, порог распада лицевой грани 0.99 → 0.91. Катет, подобранный по -нечищеному контуру, после чистки перестаёт проходить свой же критерий. - -- **Ширину штриха и узкое место меряй инструментом, а не глазом** — `sketch_measure_thickness` - ничего не строит и документа не требует, поэтому зовётся ДО эскиза - (`help(topic="silhouette-thickness")`). От этих чисел зависит, останется ли что-то от рисунка - после фаски и напечатается ли он вообще; шаг растра в ответе — точность округления катета. - -Контур, который переживёт конкретную деталь (логотип, шаблон, профиль), клади во фрагмент — -`fragment_create(path, entities)` тем же списком примитивов. Активный документ он не подменяет, -поэтому фрагмент можно снять посреди построения детали; габарит в ответе снят с самого документа — -сверяй его с задуманным. - -**Точная подгонка и отделка — в [reference/text-and-relief.md](reference/text-and-relief.md); -прочитай его, как только доходишь до точной компоновки надписи или отделки рельефа:** подбор кегля -под заданную ширину за одну пробу, ступени `widthFactor`/`height`, приросты каймы на острых -терминалах (t/sin(θ/2)) и её роль в стыковке блоков, разрядка пробелами, кайма вокруг прямоугольной -плашки, карманы `primitive(subtract)` рядом с буквами, скругления и фаски рельефа на сотнях рёбер. - -## Внешний контур: не диктуй координаты, если фигуру нельзя описать формулой - -Три маршрута, и выбор между ними механический — не по вкусу, а по происхождению геометрии. - -| Откуда фигура | Чем строить | -|---|---| -| **Вычисляется**: пластины, рёбра, сетка отверстий | `sketch_create(entities[])` — прямо в вызове, размеры вяжи переменными | -| **Срисована**: логотип, шаблон, кулачок, профиль | внешний вектор → JSON → `sketch_create(entitiesFile=…)` | -| **Пришла в CAD-формате**: файл от смежника | `import_drawing` (.dxf/.dwg) → фрагмент → `fragment_place` | - -**Обводка занимает сотни примитивов, и переписывать их в тело вызова незачем.** У `sketch_create`, -`sketch_add` и `fragment_create` есть `entitiesFile` — путь к JSON того же формата, что `entities`; -сервер читает файл сам. Замер на эмблеме Volvo: SVG в 1.5 КБ даёт 96 примитивов (50 отрезков + -46 дуг) и 9.5 КБ JSON; через `entitiesFile` это один вызов на одну строку аргументов, а эскиз -строится за один заход. Оба параметра можно задать вместе — сначала `entities`, следом файл: -так к готовому контуру дописывают рамку или ось, не трогая файл. - -Что проверить **до** постройки, а не после: габарит в ответе (`fragment_create` печатает -фактический — сверь с задуманным) и вложенные контуры. Правил заливки у эскиза КОМПАС нет: любой -вложенный контур он режет как отверстие, даже там, где вектор заливал материал. Конвертер такие -места называет заранее — на них смотри до выдавливания, иначе дыры обнаружатся на снимке. - -## Фрагмент — переиспользуемый чертёж, а не сечение - -Эскиз живёт внутри дерева построения и вне его не существует; фрагмент (*.frw) — самостоятельный -файл, который переживает деталь. `fragment_create` его пишет, `fragment_place` кладёт в активный -чертёж или другой фрагмент, а `mode` решает, чем он там станет — и это разные объекты, а не -оттенки одного: - -- **`reference`** — вставка-ссылка: содержимым владеет файл, правка файла меняет все документы, куда - он вставлен. Ради этого фрагменты и заводят: одна заготовка на десять чертежей. -- **`copy`** — копия внутри документа, живёт своей жизнью; файл потом можно удалить. -- **`local`** — вставка, переиспользуемая только внутри этого документа. -- **`explode`** — россыпь обычных примитивов: правится по одному, но связи с файлом уже нет. - -`angle` (градусы), `scale` и `mirror` задают размещение; зеркалить можно только вставку — у россыпи -отражать нечего. **Зеркало отражает относительно вертикали через точку вставки**, поэтому контур -уходит в другую сторону от неё: вставка в x=220 после `mirror` занимает 160…220, а не 220…280. - -Проверять результат — `list_drawing_objects`: он показывает состав по типам, габарит и все вставки -с их файлами. Вставка, чей файл потерялся, рисуется пустым местом — в списке она помечена -недействительной, и это единственный способ отличить её от удавшейся, не глядя на экран. +Фрагмент (*.frw, самостоятельный файл переиспользуемого контура/чертежа) — +[reference/fragments.md](reference/fragments.md). ## Сборка: собрать своё Плейбук: **создать → вставить → ЗАФИКСИРОВАТЬ базовую → сопрячь → rebuild → проверить.** -Разбор чужой сборки — следующий раздел, здесь про сборку с нуля. - -1. **Детали сначала сохрани в файлы.** Компонент вставляется ссылкой на `.m3d`/`.a3d`, поэтому - несохранённую деталь вставить нельзя. -2. **`document_create assembly` → `assembly_add_component(filePath, x, y, z, rx, ry, rz)`.** Углы — в - градусах, вокруг СОБСТВЕННЫХ осей компонента и по очереди X→Y→Z: второй угол считается уже от - повёрнутого положения. Пока задан один угол, разницы с мировыми осями нет. -3. **Зафиксируй базовую деталь СРАЗУ: `assembly_fix_component(componentIndex=0, fixedState=true)`.** КОМПАС не закрепляет - никого сам, включая первый компонент, и решатель двигает того, кого сочтёт нужным — в том числе - основание, вокруг которого ты собираешь всё остальное. Это самая частая причина «сопряжение - верное, а сборка расползлась». -4. **Сопрягай:** `assembly_add_mate(mateType, …)` — семь типов, адресация гранью и смысл - `orientation` в `help(topic="assembly-mates")`. Что решаешь ты, а не справочник: - - **Грань адресуй индексом, а не координатами:** `list_faces(component=N)` → пара - `faceIndex`+`componentIndex`. Точки на бумаге считать не нужно, и сервер сам проверит, что - грань принадлежит этому компоненту. - - **Сторону зазора у `distance` задаёт ВЫБОР ПАРЫ ГРАНЕЙ, и от стартового положения она не - зависит.** «Closest» — не «ближайшее»: при сонаправленных нормалях объект 1 садится со стороны - −n относительно объекта 2 (замерено двойным прогоном). Практическое правило: бери пару граней, - которые смотрят ДРУГ НА ДРУГА. Вышло зеркально — меняй грань, а не `orientation`. - - **Первое сопряжение вешай на зафиксированную деталь.** Пока в цепочке нет ни одного - закреплённого звена, решатель волен двигать любое; связка «каждый новый компонент — к уже - стоящему на месте» разваливается вдвое реже, чем связка «все ко всем». -5. **`rebuild`,** если авто-валидация написала «ℹ Требуется перестроение». Это НЕ ошибка построения: - так помечает себя свежий компонент или только что наложенная связь. Бросать работу здесь не надо - — правило «не продолжай при ⚠» касается строки со знаком ⚠, а не этой. - **Исключение из правила «⚠ = стоп».** После КАЖДОГО сопряжения авто-валидация пишет - «⚠ N операц. в ошибке: „Сопряжения“ (код 0: ошибки нет)». Это то же «требуется перестроение», - только под знаком ⚠: код 0 и имя операции «Сопряжения» означают, что ломаться нечему. Делай - `rebuild` и иди дальше. Настоящая беда со связью видна не здесь, а в - `describe_model(sections=mates)` — по пометке `valid:false`. -6. **Проверь:** - - `describe_model(sections=components)` — origin, поворот ЧИСЛАМИ (`rx/ry/rz` в градусах, та же - семантика, что у вставки) и «зафиксирован/свободен» у каждого: этим и видно, КОГО и НА СКОЛЬКО - подвинул решатель; - - `describe_model(sections=mates)` — что с чем связано, с каким значением и не выродилось ли. - Это ключевой детектор: `assembly_add_mate` может ответить успехом, а связь окажется - вырожденной; - - `measure(kind1=face, index1=…, componentIndex1=…, kind2=face, index2=…, componentIndex2=…)` — - зазор между деталями напрямую, без арифметики по габаритам; - - `assembly_check_interference(componentIndex1, componentIndex2)` — **не налезли ли детали друг - на друга.** Проверяй все пары, которые должны идти впритирку. - -**Неудачную связь не нужно пересобирать с нуля:** `assembly_delete(target="mate"|"component", index)` -снимает сопряжение или компонент вместе с опирающимися на него связями. Индексы после удаления -сдвигаются — иди от больших к меньшим и перечитывай `describe_model(sections=mates)`. - -**Пересечение деталей не видно НИГДЕ, кроме `assembly_check_interference`.** Сборка складывается из -готовых тел, и КОМПАС их не вычитает: две детали могут занимать общий объём, а сопряжения при этом -останутся `valid`, раздел `errors` — пустым, объём экспорта — простой суммой компонентов. `measure` -тоже не спасает: нулевой зазор одинаково означает и касание, и взаимопроникновение. Проверяй все -пары, которые должны идти впритирку. - -**«Тела: 0» в сборке означает не «пусто», а «спрошено не то»:** тела принадлежат компонентам -(`describe_model(component=N, …)`), а `box` и `mass` без `component` честно дают габарит и МЦХ сборки -целиком. Про разные системы координат внутри паспорта компонента — -`help(topic="model-inspection")`; про локальные координаты граней и `pointOnFace` — -`help(topic="face-addressing")`. +Сборка с нуля целиком, с адресацией граней, семью типами сопряжений и порядком проверки — +[reference/assembly-playbook.md](reference/assembly-playbook.md). Разбор чужой сборки (пришедшей +STEP/импортом) — следующий раздел. ## Чертёж: оформить деталь по ГОСТ -Чертёж строится не так, как деталь: **авто-валидации здесь нет**. Правило трёх проверок из §«Базовый -цикл» в 2D не работает — операция не отчитывается «построилось верно», потому что верность тут не -про геометрию, а про место на листе. Проверять приходится самому, и способ ровно один: посмотреть. - -**Порядок, который окупается:** - -1. **Деталь строится в чертёжной ориентации.** Набор видов задаётся ОТ модели: `mainOrientation` - выбирает, какую ориентацию модели показывает главный вид, но саму ориентацию модели он не - вращает. Если самая информативная проекция детали — не одна из шести стандартных, чертежа по - ГОСТ не выйдет; планируешь чертёж — строй эскизы так, чтобы главный вид попадал на плоскость - с наибольшей информацией. -2. **`drawing_set_sheet_format`** (формат до видов — иначе виды придётся двигать), при - необходимости `drawing_add_sheet` для второго листа. -3. **`drawing_create_standard_views(views=[…], mainOrientation=…, hiddenLines=…)`** — бери ровно те - проекции, которые нужны: лишний вид — лишнее место и лишний повод ошибиться видом. **Главный вид - входит в набор сам**: перечисленные коды — это проекции ОТНОСИТЕЛЬНО него, поэтому `views=["top"]` - даёт ДВА вида (главный и сверху). Ответ возвращает НОМЕРА видов, и дальше всё адресуется ими. -4. **Координаты — замером, а не формулой.** `drawing_get_view_info(viewNumber)` отдаёт паспорт вида - одним вызовом: положение на листе, масштаб, габарит и — главное — локальную СК: куда попадает - начало координат модели и куда смотрят её оси +X/+Y/+Z в координатах вида (нулевой орт = ось - проекции, вдоль неё вид смотрит). Этого хватает, чтобы пересчитывать точки модели в вид - арифметикой; отдельные точки переводит `drawing_project_point(x, y, z, viewNumber)`. - Выводить оси из головы не надо и вредно: у КОМПАС «спереди» показывает плоскость XY детали, а - «сверху» — X и **минус Z**, и **знаки инверсии зависят от `mainOrientation`** — формула, - выведенная на одном чертеже, на соседнем врёт. -5. **Разрез — до размеров, а не после.** Внутреннюю геометрию (зенковка, ступенчатое отверстие, - паз, глубокая расточка, полость) показывай разрезом, а не невидимыми линиями: по штриховой линии - нельзя ни поставить нормальный размер, ни разобрать форму. `drawing_add_section_view` — см. - §«Разрез» ниже. Размеры внутренней геометрии ставятся УЖЕ НА РАЗРЕЗЕ, поэтому строить его надо - раньше простановки, иначе размеры придётся переносить. -6. **Размеры**, `drawing_add_axis` (осевые и центровые), шероховатость, выноски, техтребования, - штамп. **Осевые сначала посмотри, потом добивай**: ассоциативные виды приходят со СВОИМИ - центровыми и осевыми, которые КОМПАС ставит сам, и `drawing_add_axis` вслепую даёт дубли поверх - существующих. Сначала `list_drawing_objects(kind="axis"|"centreMarker", viewNumber=…)`. -7. **`drawing_export_image`** — посмотреть глазами. Пока картинки не было, чертёж не сдан. - -### Разрез - -`drawing_add_section_view` делает всё сразу: проводит линию разреза на базовом виде и строит по ней -производный вид со штриховкой. Механика, ступенчатый разрез и встроенная проверка честности — -`help(topic="drawing-sections")`; здесь только решения, которые принимаешь ты: - -- **Разрез или `hiddenLines`.** Разрез — когда внутреннее нужно ИЗМЕРИТЬ или показать форму: - зенковка и цековка, ступенчатое или резьбовое отверстие, паз, канавка, глубокая расточка, тонкая - стенка полой детали. Невидимые линии — только намёк на то, что уже показано где-то ещё; размер к - штриховой линии не привязывают. -- **Веди линию с запасом за габарит** (плита 60 мм → от −40 до +40): линия по самому краю режет не - всю деталь. Координаты — в ЛСК БАЗОВОГО вида, её даёт `drawing_get_view_info`. -- **Строй разрез, когда модель готова**: после правки детали перестраивается только ПЕРВЫЙ разрез - базового вида. Модель всё же изменилась — снеси разрезы и построй заново. -- Сам разрез дальше — обычный вид: размеры и обозначения ставятся на него по его номеру. - -**Промах простановки лечится, а не остаётся навсегда.** Отката (undo) у чертежа нет, но -`list_drawing_objects` показывает обозначения с индексами, `drawing_delete_object` убирает одно, а -`drawing_move_object` его СДВИГАЕТ — слипшиеся тексты соседних размеров лечат именно сдвигом, а не -заменой размера на выноску. Так же правится и компоновка: `drawing_move_view` переставляет вид со -всеми обозначениями, `drawing_add_view` доносит недостающую проекцию, `drawing_delete_view` убирает -лишнюю. Пересоздавать документ ради компоновки не нужно. Подробности адресации и порядок удаления — -`help(topic="drawing-fixing")`. - -**Три вещи, на которых чертёж молча выходит неверным** — держи их в голове, детали в справочнике: - -- `viewNumber=0` — это НЕ главный вид, а координаты ЛИСТА; знак, поставленный «на главном виде» с - нулём, уезжает за границы формата (`help(topic="drawing-views")`); -- ассоциативны только диаметральный и радиальный размеры; линейный и угловой меряют координаты, и - «60» останется, когда деталь станет 70 — после правки модели перепроверяй их сам - (`help(topic="drawing-dimensions")`); -- допуск, не легший в размер, виден только по пустому read-back в ответе — техтребования его не - заменят (`help(topic="drawing-dimension-text")`). - -Ещё две ловушки, о которых узнаёшь только столкнувшись: штамп частично ведёт МОДЕЛЬ, а не чертёж -(`help(topic="drawing-title-block")`), и у цилиндрической грани нет «координат оси» — её -восстанавливают и проверяют сходимостью площадей (`help(topic="cylinder-axis")`). - -**Чего нет.** Выносного элемента (увеличенный фрагмент), МЕСТНОГО разреза внутри вида, местного -вида и вида с разрывом. Ассоциативного ЛИНЕЙНОГО размера нет и не будет: в API у него нет привязки к геометрии -ни под каким именем (проверено QI, поздним связыванием и размером с обрывом). Экспорта в PDF/DXF -тоже нет — наружу уходит растр (`drawing_export_image`) и STEP. +Чертёж строится не так, как деталь: **авто-валидации здесь нет** — верность тут не про геометрию, +а про место на листе, и проверяется только просмотром. Порядок «формат → виды → координаты замером +→ разрез до размеров → простановка → `drawing_export_image`», разрез, три ловушки, на которых +чертёж молча выходит неверным (`viewNumber=0`, ассоциативность размеров, допуск без read-back), и +чего в 2D нет — [reference/drawing-playbook.md](reference/drawing-playbook.md). ## Работа с импортом / сборками -Конвейер «импорт → разбор → извлечение детали → осмотр → модификация → экспорт»: - -1. **`import_model`** — `type` задаёт документ: `assembly` (по умолчанию) или `part`. - Формат берётся из расширения; `.prt` и `.asm` носят несколько САПР сразу — там задавай `format` - явно. Нужна отдельная деталь файлом — включай `createComponentFiles`: без него импорт не пишет - на диск ничего, кроме открытого документа. - Плоский чертёж (.dxf/.dwg) читает не он, а `import_drawing`. -2. **Разбор:** `describe_model(sections=components)` (у каждого компонента — origin, поворот, - фиксация) и `describe_model(sections=mates)`, если сборка пришла со связями; грани компонента — - `list_faces(component=N)`. Извлечённую деталь открывай самостоятельным документом. - Гашение видимости компонента на снимок **не влияет** — изоляция так не делается. -3. **Осмотр — структурно** (`describe_model`: какая ось «высота», МЦХ, топология). Снимок - `model_snapshot(view=…)` — ракурс выбирай по плоскости детали (плоскую смотри сверху или - спереди, не в изометрии); один и тот же `view` даёт воспроизводимый кадр «до/после». - **Пары ракурсов противоположны по оси:** `front` смотрит из +Z, `rear` — из −Z, так же - `top`/`bottom` и `left`/`right`. Следствие, которое нужно чаще всего: деталь, лежащая на столе - гранью Z=0 (эскиз на XOY, выдавливание вперёд), видна столу как **`rear`**, а `front` покажет - зеркальную картинку. Читаемость надписи или логотипа доказывают снимком с нужной стороны, а не - рассуждением. -4. **Модификация «тупой» B-rep** (итог — в ответе каждой операции): простой случай — `move_face`; - вставка N мм в призматическую ножку — `split_solid_by_plane` → `move_body` → `move_face` (мост) - → `boolean_union` (шаги пронумерованы в описаниях самих инструментов; цепочка проверена - end-to-end: проставка 39.45 → 41.45 мм, построение чистое). -5. **Перед выдачей:** последняя операция «Построение чистое» → `export_model` (STEP для обмена, - STL для печати). +Конвейер «импорт → разбор → извлечение детали → осмотр → модификация → экспорт» для ЧУЖОЙ модели — +[reference/import-playbook.md](reference/import-playbook.md). ## Исполнения: одна модель — несколько геометрий -Типоразмеры (профиль на 100, 200, 500 мм), правое и левое зеркало, версии с отверстием и без часто -живут **в одном файле** исполнениями. Тогда всё, что ты меришь и экспортируешь, относится к -**текущему** исполнению — не к «детали вообще». - -- **Признак замечаешь не ты, а ответ.** У модели с несколькими исполнениями `describe_model` сам - пишет, сколько их и какое сейчас текущее. Увидел эту строку — прежде чем считать массу или резать - чертёж, посмотри `describe_model(sections=embodiments)`: там габарит и масса **каждого**, и это - чтение ничего не переключает. -- **Переключение равно смене документа.** `embodiment(action="set", target="L200")` обнуляет - сессию построения: хендлы `sk_…`/`op_…` после него мертвы. Сохраняй документ до переключения. -- **Индекс живёт до первой правки дерева**: после `delete` индексы смещаются. Адресуй обозначением - («L500»), а индексом — только сразу после того, как увидел список. -- **Строишь новое исполнение** — `embodiment(action="add", number="02")`; оно сразу становится - текущим, строй в нём немедленно. `depended=false` нужен, когда правки НЕ должны наследоваться от - родителя, — иначе изменение уедет и в него. -- **В сборку исполнение выбирается явно**: `assembly_add_component(embodiment="L500")`. Без - параметра встанет **базовое** исполнение, а не то, что сохранено в файле текущим. Обозначение - нужно ПОЛНОЕ («ПРОБА.001-01», а не «-01»); сервер откажет, если КОМПАС подсунет базовое. -- **«Удлинить эти профили с 500 до 600» — это смена исполнения экземпляра, а не новая вставка**: - `embodiment(action="set", target="L600", component=N)` и затем `rebuild`. Сопряжения при этом - **сохраняются** — пересоздание вставки, наоборот, порвало бы их все. Какие исполнения есть у - конкретного экземпляра, показывает `describe_model(sections="embodiments", component=N)`. -- **Выгнать всю линейку на печать** — `export_model(embodiment="all")`: каждое исполнение в свой - файл с суффиксом обозначения. -- **После смены исполнения в собранном узле проверь сопряжения** (`sections="mates"`): если у нового - исполнения нет объекта, на который опиралась связь, она рвётся — «ССЫЛКА ПОТЕРЯНА», `valid: false`, - компонент недоопределён. Что было потеряно, хранит имя сопряжения; `assembly_delete` сотрёт и его. - -Подробности и границы — `help(topic="embodiments")`. +Типоразмеры, зеркала, версии с отверстием и без в одном файле — переключение равно смене документа +(хендлы гибнут), индексация и адресация в сборке, порядок «сначала геометрия, исполнения последним +шагом» — [reference/embodiments-playbook.md](reference/embodiments-playbook.md). ## Эвристики и подводные камни @@ -550,6 +310,28 @@ variables | errors | components` (`sections` сужает и ответ, и об - **Направление операции** (`extrude(forward)`, знак `distance` у `move_face`): на выбранной грани зависит от ориентации её нормали — реши ДО построения, прочитав нормаль в `list_faces(index=N)` (или по снимку), а после сверь сводку объёма в ответе. +- **У ВЫРЕЗА `forward` смотрит в другую сторону, чем у прилива.** Замерено на плите Z 0..10 с + эскизом на плоскости z=10: `extrude(mode=boss, forward=true)` наращивает ВВЕРХ, а + `extrude(mode=cut, forward=true)` режет ВНИЗ, в тело. То есть у выреза «прямое» — это внутрь + материала. Оба умолчания делают то, чего обычно и хотят, но рассуждение «forward — это плюс + нормали» даст перевёрнутый карман. **Вырез, ушедший в пустоту, КОМПАС считает успехом** — + дерево чистое, объём не изменился; ловит это только строка сверки в ответе. +- **На YOZ деталь растёт в МИНУС X, и туда же уезжает смещение плоскости.** Нормаль базовой + плоскости смотрит в плюс своей оси не всегда — замерено: XOY → +Z, XOZ → +Y, а **YOZ → −X**. + Значит `extrude(forward=true)` по эскизу на YOZ кладёт материал в отрицательные X, а + `sketch_create(plane="YOZ", offset=30)` ставит плоскость в X = −30, а не +30. Обе вещи следуют + одной нормали, так что путаницы между ними нет — но знак учитывай при компоновке, иначе деталь + уедет зеркально задуманному. Таблица целиком — `help(topic="build-verification")`. +- **`sketch_update` несущего эскиза МОЛЧА теряет часть отделки.** Фаска и скругление, снятые по + точкам, после переиздания базового контура остаются в дереве, ошибок не дают — а построены + оказываются не на всех рёбрах: замерено трижды на одной детали, из четырёх скруглений выживали + то два, то одно. Иногда КОМПАС всё же сознаётся («код 145: операция потеряла опорные объекты»), + но чаще молчит, и единственный признак — объём: он оказывается БОЛЬШЕ ожидаемого ровно на + недоснятый материал. Порядок после каждого переиздания несущего эскиза: сверить объём с прикидкой + либо пересчитать результат отделки (`list_faces(type="cylinder")` для скруглений — их должно быть + ровно столько, сколько рёбер в пакете); недосчитались — снести отделочные узлы + (`feature_delete(featureIndices=[…])`) и построить заново, уже по новым рёбрам. Пересборка + дешевле поиска виновного, а «построение чистое» здесь не значит ничего. - **Эскизы и операции адресуются ХЕНДЛАМИ из ответов** — `sk_7c1e5aa3f1_1_2`, `op_7c1e5aa3f1_1_5`. Не сочиняй их и не передавай числа: числовая адресация не поддерживается, а сервер объяснит формат отказом. Хендл живёт только в текущей сессии построения: смена активного документа @@ -588,9 +370,11 @@ variables | errors | components` (`sections` сужает и ответ, и об ## Открытые вопросы / границы -- **Параметрика работает через параметры ОПЕРАЦИЙ, а не размеры эскиза** — сдвинуть контур - переменной нельзя, только пересобрать эскиз. Планируй членение так, чтобы изменяемое задавалось - параметром операции (глубина, радиус, габарит примитива), а не координатами в эскизе. +- **Параметрика работает через параметры ОПЕРАЦИЙ, а не координаты ВНУТРИ эскиза** — сдвинуть + контур переменной нельзя, только пересобрать эскиз (`sketch_update`). Но это ограничение уже + границей: **плоскость** эскиза — обычный узел дерева с параметром «Расстояние», и она + параметризуется как всё остальное. Планируй членение так, чтобы изменяемое задавалось параметром + операции (глубина, радиус, габарит примитива) или положением плоскости, а не координатами точек. - **2D покрыт вместе с разрезом и сечением.** Есть виды (с выбором набора, ориентации главного и невидимых линий), разрез/сечение, листы, штамп, размеры с допусками, осевые, обозначения, техтребования, удаление и сдвиг объекта, вывод в растр — методика в §«Чертёж». Нет выносного diff --git a/plugin/skills/kompas-3d/reference/assembly-playbook.md b/plugin/skills/kompas-3d/reference/assembly-playbook.md new file mode 100644 index 0000000..3af34ec --- /dev/null +++ b/plugin/skills/kompas-3d/reference/assembly-playbook.md @@ -0,0 +1,63 @@ +# Сборка: собрать своё + +Дополнение к SKILL.md — методика для сборки С НУЛЯ. Разбор ЧУЖОЙ сборки (пришедшей STEP/импортом) — +`reference/import-playbook.md`. + +Плейбук: **создать → вставить → ЗАФИКСИРОВАТЬ базовую → сопрячь → rebuild → проверить.** + +1. **Детали сначала сохрани в файлы.** Компонент вставляется ссылкой на `.m3d`/`.a3d`, поэтому + несохранённую деталь вставить нельзя. +2. **`document_create assembly` → `assembly_add_component(filePath, x, y, z, rx, ry, rz)`.** Углы — в + градусах, вокруг СОБСТВЕННЫХ осей компонента и по очереди X→Y→Z: второй угол считается уже от + повёрнутого положения. Пока задан один угол, разницы с мировыми осями нет. +3. **Зафиксируй базовую деталь СРАЗУ: `assembly_fix_component(componentIndex=0, fixedState=true)`.** КОМПАС не закрепляет + никого сам, включая первый компонент, и решатель двигает того, кого сочтёт нужным — в том числе + основание, вокруг которого ты собираешь всё остальное. Это самая частая причина «сопряжение + верное, а сборка расползлась». +4. **Сопрягай:** `assembly_add_mate(mateType, …)` — семь типов, адресация гранью и смысл + `orientation` в `help(topic="assembly-mates")`. Что решаешь ты, а не справочник: + - **Грань адресуй индексом, а не координатами:** `list_faces(component=N)` → пара + `faceIndex`+`componentIndex`. Точки на бумаге считать не нужно, и сервер сам проверит, что + грань принадлежит этому компоненту. + - **Сторону зазора у `distance` задаёт ВЫБОР ПАРЫ ГРАНЕЙ, и от стартового положения она не + зависит.** «Closest» — не «ближайшее»: при сонаправленных нормалях объект 1 садится со стороны + −n относительно объекта 2 (замерено двойным прогоном). Практическое правило: бери пару граней, + которые смотрят ДРУГ НА ДРУГА. Вышло зеркально — меняй грань, а не `orientation`. + - **Первое сопряжение вешай на зафиксированную деталь.** Пока в цепочке нет ни одного + закреплённого звена, решатель волен двигать любое; связка «каждый новый компонент — к уже + стоящему на месте» разваливается вдвое реже, чем связка «все ко всем». +5. **`rebuild`,** если авто-валидация написала «ℹ Требуется перестроение». Это НЕ ошибка построения: + так помечает себя свежий компонент или только что наложенная связь. Бросать работу здесь не надо + — правило «не продолжай при ⚠» касается строки со знаком ⚠, а не этой. + **Исключение из правила «⚠ = стоп».** После КАЖДОГО сопряжения авто-валидация пишет + «⚠ N операц. в ошибке: „Сопряжения“ (код 0: ошибки нет)». Это то же «требуется перестроение», + только под знаком ⚠: код 0 и имя операции «Сопряжения» означают, что ломаться нечему. Делай + `rebuild` и иди дальше. Настоящая беда со связью видна не здесь, а в + `describe_model(sections=mates)` — по пометке `valid:false`. +6. **Проверь:** + - `describe_model(sections=components)` — origin, поворот ЧИСЛАМИ (`rx/ry/rz` в градусах, та же + семантика, что у вставки) и «зафиксирован/свободен» у каждого: этим и видно, КОГО и НА СКОЛЬКО + подвинул решатель; + - `describe_model(sections=mates)` — что с чем связано, с каким значением и не выродилось ли. + Это ключевой детектор: `assembly_add_mate` может ответить успехом, а связь окажется + вырожденной; + - `measure(kind1=face, index1=…, componentIndex1=…, kind2=face, index2=…, componentIndex2=…)` — + зазор между деталями напрямую, без арифметики по габаритам; + - `assembly_check_interference(componentIndex1, componentIndex2)` — **не налезли ли детали друг + на друга.** Проверяй все пары, которые должны идти впритирку. + +**Неудачную связь не нужно пересобирать с нуля:** `assembly_delete(target="mate"|"component", index)` +снимает сопряжение или компонент вместе с опирающимися на него связями. Индексы после удаления +сдвигаются — иди от больших к меньшим и перечитывай `describe_model(sections=mates)`. + +**Пересечение деталей не видно НИГДЕ, кроме `assembly_check_interference`.** Сборка складывается из +готовых тел, и КОМПАС их не вычитает: две детали могут занимать общий объём, а сопряжения при этом +останутся `valid`, раздел `errors` — пустым, объём экспорта — простой суммой компонентов. `measure` +тоже не спасает: нулевой зазор одинаково означает и касание, и взаимопроникновение. Проверяй все +пары, которые должны идти впритирку. + +**«Тела: 0» в сборке означает не «пусто», а «спрошено не то»:** тела принадлежат компонентам +(`describe_model(component=N, …)`), а `box` и `mass` без `component` честно дают габарит и МЦХ сборки +целиком. Про разные системы координат внутри паспорта компонента — +`help(topic="model-inspection")`; про локальные координаты граней и `pointOnFace` — +`help(topic="face-addressing")`. diff --git a/plugin/skills/kompas-3d/reference/drawing-playbook.md b/plugin/skills/kompas-3d/reference/drawing-playbook.md new file mode 100644 index 0000000..8c8d20f --- /dev/null +++ b/plugin/skills/kompas-3d/reference/drawing-playbook.md @@ -0,0 +1,81 @@ +# Чертёж: оформить деталь по ГОСТ + +Дополнение к SKILL.md — методика оформления 2D-чертежа. Чертёж строится не так, как деталь: +**авто-валидации здесь нет**. Правило трёх проверок из SKILL.md §«Два правила прежде всего» в 2D не +работает — операция не отчитывается «построилось верно», потому что верность тут не про геометрию, +а про место на листе. Проверять приходится самому, и способ ровно один: посмотреть. + +**Порядок, который окупается:** + +1. **Деталь строится в чертёжной ориентации.** Набор видов задаётся ОТ модели: `mainOrientation` + выбирает, какую ориентацию модели показывает главный вид, но саму ориентацию модели он не + вращает. Если самая информативная проекция детали — не одна из шести стандартных, чертежа по + ГОСТ не выйдет; планируешь чертёж — строй эскизы так, чтобы главный вид попадал на плоскость + с наибольшей информацией. +2. **`drawing_set_sheet_format`** (формат до видов — иначе виды придётся двигать), при + необходимости `drawing_add_sheet` для второго листа. +3. **`drawing_create_standard_views(views=[…], mainOrientation=…, hiddenLines=…)`** — бери ровно те + проекции, которые нужны: лишний вид — лишнее место и лишний повод ошибиться видом. **Главный вид + входит в набор сам**: перечисленные коды — это проекции ОТНОСИТЕЛЬНО него, поэтому `views=["top"]` + даёт ДВА вида (главный и сверху). Ответ возвращает НОМЕРА видов, и дальше всё адресуется ими. +4. **Координаты — замером, а не формулой.** `drawing_get_view_info(viewNumber)` отдаёт паспорт вида + одним вызовом: положение на листе, масштаб, габарит и — главное — локальную СК: куда попадает + начало координат модели и куда смотрят её оси +X/+Y/+Z в координатах вида (нулевой орт = ось + проекции, вдоль неё вид смотрит). Этого хватает, чтобы пересчитывать точки модели в вид + арифметикой; отдельные точки переводит `drawing_project_point(x, y, z, viewNumber)`. + Выводить оси из головы не надо и вредно: у КОМПАС «спереди» показывает плоскость XY детали, а + «сверху» — X и **минус Z**, и **знаки инверсии зависят от `mainOrientation`** — формула, + выведенная на одном чертеже, на соседнем врёт. +5. **Разрез — до размеров, а не после.** Внутреннюю геометрию (зенковка, ступенчатое отверстие, + паз, глубокая расточка, полость) показывай разрезом, а не невидимыми линиями: по штриховой линии + нельзя ни поставить нормальный размер, ни разобрать форму. `drawing_add_section_view` — см. + §«Разрез» ниже. Размеры внутренней геометрии ставятся УЖЕ НА РАЗРЕЗЕ, поэтому строить его надо + раньше простановки, иначе размеры придётся переносить. +6. **Размеры**, `drawing_add_axis` (осевые и центровые), шероховатость, выноски, техтребования, + штамп. **Осевые сначала посмотри, потом добивай**: ассоциативные виды приходят со СВОИМИ + центровыми и осевыми, которые КОМПАС ставит сам, и `drawing_add_axis` вслепую даёт дубли поверх + существующих. Сначала `list_drawing_objects(kind="axis"|"centreMarker", viewNumber=…)`. +7. **`drawing_export_image`** — посмотреть глазами. Пока картинки не было, чертёж не сдан. + +## Разрез + +`drawing_add_section_view` делает всё сразу: проводит линию разреза на базовом виде и строит по ней +производный вид со штриховкой. Механика, ступенчатый разрез и встроенная проверка честности — +`help(topic="drawing-sections")`; здесь только решения, которые принимаешь ты: + +- **Разрез или `hiddenLines`.** Разрез — когда внутреннее нужно ИЗМЕРИТЬ или показать форму: + зенковка и цековка, ступенчатое или резьбовое отверстие, паз, канавка, глубокая расточка, тонкая + стенка полой детали. Невидимые линии — только намёк на то, что уже показано где-то ещё; размер к + штриховой линии не привязывают. +- **Веди линию с запасом за габарит** (плита 60 мм → от −40 до +40): линия по самому краю режет не + всю деталь. Координаты — в ЛСК БАЗОВОГО вида, её даёт `drawing_get_view_info`. +- **Строй разрез, когда модель готова**: после правки детали перестраивается только ПЕРВЫЙ разрез + базового вида. Модель всё же изменилась — снеси разрезы и построй заново. +- Сам разрез дальше — обычный вид: размеры и обозначения ставятся на него по его номеру. + +**Промах простановки лечится, а не остаётся навсегда.** Отката (undo) у чертежа нет, но +`list_drawing_objects` показывает обозначения с индексами, `drawing_delete_object` убирает одно, а +`drawing_move_object` его СДВИГАЕТ — слипшиеся тексты соседних размеров лечат именно сдвигом, а не +заменой размера на выноску. Так же правится и компоновка: `drawing_move_view` переставляет вид со +всеми обозначениями, `drawing_add_view` доносит недостающую проекцию, `drawing_delete_view` убирает +лишнюю. Пересоздавать документ ради компоновки не нужно. Подробности адресации и порядок удаления — +`help(topic="drawing-fixing")`. + +**Три вещи, на которых чертёж молча выходит неверным** — держи их в голове, детали в справочнике: + +- `viewNumber=0` — это НЕ главный вид, а координаты ЛИСТА; знак, поставленный «на главном виде» с + нулём, уезжает за границы формата (`help(topic="drawing-views")`); +- ассоциативны только диаметральный и радиальный размеры; линейный и угловой меряют координаты, и + «60» останется, когда деталь станет 70 — после правки модели перепроверяй их сам + (`help(topic="drawing-dimensions")`); +- допуск, не легший в размер, виден только по пустому read-back в ответе — техтребования его не + заменят (`help(topic="drawing-dimension-text")`). + +Ещё две ловушки, о которых узнаёшь только столкнувшись: штамп частично ведёт МОДЕЛЬ, а не чертёж +(`help(topic="drawing-title-block")`), и у цилиндрической грани нет «координат оси» — её +восстанавливают и проверяют сходимостью площадей (`help(topic="cylinder-axis")`). + +**Чего нет.** Выносного элемента (увеличенный фрагмент), МЕСТНОГО разреза внутри вида, местного +вида и вида с разрывом. Ассоциативного ЛИНЕЙНОГО размера нет и не будет: в API у него нет привязки к геометрии +ни под каким именем (проверено QI, поздним связыванием и размером с обрывом). Экспорта в PDF/DXF +тоже нет — наружу уходит растр (`drawing_export_image`) и STEP. diff --git a/plugin/skills/kompas-3d/reference/embodiments-playbook.md b/plugin/skills/kompas-3d/reference/embodiments-playbook.md new file mode 100644 index 0000000..8c8e5ad --- /dev/null +++ b/plugin/skills/kompas-3d/reference/embodiments-playbook.md @@ -0,0 +1,36 @@ +# Исполнения: одна модель — несколько геометрий + +Дополнение к SKILL.md. Типоразмеры (профиль на 100, 200, 500 мм), правое и левое зеркало, версии с +отверстием и без часто живут **в одном файле** исполнениями. Тогда всё, что ты меришь и +экспортируешь, относится к **текущему** исполнению — не к «детали вообще». + +- **Признак замечаешь не ты, а ответ.** У модели с несколькими исполнениями `describe_model` сам + пишет, сколько их и какое сейчас текущее. Увидел эту строку — прежде чем считать массу или резать + чертёж, посмотри `describe_model(sections=embodiments)`: там габарит и масса **каждого**, и это + чтение ничего не переключает. +- **Переключение равно смене документа.** `embodiment(action="set", target="L200")` обнуляет + сессию построения: хендлы `sk_…`/`op_…` после него мертвы. Сохраняй документ до переключения. +- **Индекс живёт до первой правки дерева**: после `delete` индексы смещаются. Адресуй обозначением + («L500»), а индексом — только сразу после того, как увидел список. У **варианта** обозначение + родительское, и адресовать его номером нельзя вовсе — под «-01» подходят двое; зови его по имени + («M5-01») или индексом. +- **Исполнения делай ПОСЛЕДНИМ шагом.** Переключение убивает хендлы, и дальнейшая правка дерева + идёт уже через `feature_delete(featureIndex=…)` по `describe_model(sections=tree)` — путь рабочий, + но менее удобный. Сначала доведи базовую геометрию и параметризацию, потом размножай. +- **Строишь новое исполнение** — `embodiment(action="add", number="02")`; оно сразу становится + текущим, строй в нём немедленно. `depended=false` нужен, когда правки НЕ должны наследоваться от + родителя, — иначе изменение уедет и в него. +- **В сборку исполнение выбирается явно**: `assembly_add_component(embodiment="L500")`. Без + параметра встанет **базовое** исполнение, а не то, что сохранено в файле текущим. Обозначение + нужно ПОЛНОЕ («ПРОБА.001-01», а не «-01»); сервер откажет, если КОМПАС подсунет базовое. +- **«Удлинить эти профили с 500 до 600» — это смена исполнения экземпляра, а не новая вставка**: + `embodiment(action="set", target="L600", component=N)` и затем `rebuild`. Сопряжения при этом + **сохраняются** — пересоздание вставки, наоборот, порвало бы их все. Какие исполнения есть у + конкретного экземпляра, показывает `describe_model(sections="embodiments", component=N)`. +- **Выгнать всю линейку на печать** — `export_model(embodiment="all")`: каждое исполнение в свой + файл с суффиксом обозначения. +- **После смены исполнения в собранном узле проверь сопряжения** (`sections="mates"`): если у нового + исполнения нет объекта, на который опиралась связь, она рвётся — «ССЫЛКА ПОТЕРЯНА», `valid: false`, + компонент недоопределён. Что было потеряно, хранит имя сопряжения; `assembly_delete` сотрёт и его. + +Подробности и границы — `help(topic="embodiments")`. diff --git a/plugin/skills/kompas-3d/reference/fragments.md b/plugin/skills/kompas-3d/reference/fragments.md new file mode 100644 index 0000000..c1627c2 --- /dev/null +++ b/plugin/skills/kompas-3d/reference/fragments.md @@ -0,0 +1,25 @@ +# Фрагмент — переиспользуемый чертёж, а не сечение + +Дополнение к SKILL.md. Эскиз живёт внутри дерева построения и вне его не существует; фрагмент (*.frw) +— самостоятельный файл, который переживает деталь. `fragment_create` его пишет, `fragment_place` +кладёт в активный чертёж или другой фрагмент, а `mode` решает, чем он там станет — и это разные +объекты, а не оттенки одного: + +- **`reference`** — вставка-ссылка: содержимым владеет файл, правка файла меняет все документы, куда + он вставлен. Ради этого фрагменты и заводят: одна заготовка на десять чертежей. +- **`copy`** — копия внутри документа, живёт своей жизнью; файл потом можно удалить. +- **`local`** — вставка, переиспользуемая только внутри этого документа. +- **`explode`** — россыпь обычных примитивов: правится по одному, но связи с файлом уже нет. + +`angle` (градусы), `scale` и `mirror` задают размещение; зеркалить можно только вставку — у россыпи +отражать нечего. **Зеркало отражает относительно вертикали через точку вставки**, поэтому контур +уходит в другую сторону от неё: вставка в x=220 после `mirror` занимает 160…220, а не 220…280. + +Проверять результат — `list_drawing_objects`: он показывает состав по типам, габарит и все вставки +с их файлами. Вставка, чей файл потерялся, рисуется пустым местом — в списке она помечена +недействительной, и это единственный способ отличить её от удавшейся, не глядя на экран. + +Контур, который переживёт конкретную деталь (логотип, шаблон, профиль), клади во фрагмент — +`fragment_create(path, entities)` тем же списком примитивов. Активный документ он не подменяет, +поэтому фрагмент можно снять посреди построения детали; габарит в ответе снят с самого документа — +сверяй его с задуманным. diff --git a/plugin/skills/kompas-3d/reference/import-playbook.md b/plugin/skills/kompas-3d/reference/import-playbook.md new file mode 100644 index 0000000..c0f8474 --- /dev/null +++ b/plugin/skills/kompas-3d/reference/import-playbook.md @@ -0,0 +1,58 @@ +# Работа с импортом / сборками + +Дополнение к SKILL.md — конвейер разбора ЧУЖОЙ модели/сборки: «импорт → разбор → извлечение детали → +осмотр → модификация → экспорт». Сборка С НУЛЯ — `reference/assembly-playbook.md`. + +1. **`import_model`** — `type` задаёт документ: `assembly` (по умолчанию) или `part`. + Формат берётся из расширения; `.prt` и `.asm` носят несколько САПР сразу — там задавай `format` + явно. Нужна отдельная деталь файлом — включай `createComponentFiles` (по умолчанию `false`). + **Выключенный переключатель чистого диска НЕ гарантирует:** конвертер пишет файлы рядом с + ИСХОДНИКОМ и помимо него — замерено, четыре импорта положили четыре незапрошенных `.m3d`. + Что реально изменилось в каталоге исходника, ответ перечисляет поимённо; не хочешь засорять + чужой каталог — копируй файл во временный (`help(topic="model-exchange")`). + **STL/OBJ приходят СЕТКОЙ:** «тел 0, объём 0.0 мм³» после такого импорта — норма, а не провал, + проверяй габаритом; ответ говорит это вслух и называет число треугольников из самого файла. + Плоский чертёж (.dxf/.dwg) читает не он, а `import_drawing`. +2. **Разбор:** `describe_model(sections=components)` (у каждого компонента — origin, поворот, + фиксация) и `describe_model(sections=mates)`, если сборка пришла со связями; грани компонента — + `list_faces(component=N)`. Извлечённую деталь открывай самостоятельным документом. + Гашение видимости компонента на снимок **не влияет** — изоляция так не делается. +3. **Осмотр — структурно** (`describe_model`: какая ось «высота», МЦХ, топология). Снимок + `model_snapshot(view=…)` — ракурс выбирай по плоскости детали (плоскую смотри сверху или + спереди, не в изометрии); один и тот же `view` даёт воспроизводимый кадр «до/после». + **Пары ракурсов противоположны по оси:** `front` смотрит из +Z, `rear` — из −Z, так же + `top`/`bottom` и `left`/`right`. Следствие, которое нужно чаще всего: деталь, лежащая на столе + гранью Z=0 (эскиз на XOY, выдавливание вперёд), видна столу как **`rear`**, а `front` покажет + зеркальную картинку. Читаемость надписи или логотипа доказывают снимком с нужной стороны, а не + рассуждением. + **Но «ортогонально» — правило про ПЛОСКУЮ деталь.** Как только форма выходит из одной плоскости + (кронштейн, косынка, изогнутый профиль), ортогональный кадр перестаёт читаться: он плоский, + подписей осей на нём нет, и сопоставить увиденное с координатами не выходит — `right` даёт + картинку, по которой не сказать, где перёд. Профиль такой детали смотри `view="iso"`: там видно, + что куда загнуто. +4. **Модификация «тупой» B-rep** (итог — в ответе каждой операции). Инструмент выбирается по тому, + ЧТО задано в требовании, а не по тому, что привычнее: + + | Требование звучит как | Инструмент | Почему не `move_face` | + |---|---|---| + | «сдвинуть грань на N мм» | `move_face` | это он и есть | + | «диаметр отверстия (бобышки) должен стать D» | **`resize_face`** | задаётся ДИАМЕТР, ось не двигается; `move_face` сдвинул бы стенку на (D−d)/2, и это пришлось бы считать самому | + | «грань должна встать НА объект: на плоскость, на грань соседнего тела» | **`replace_face`** | расстояние считает КОМПАС; `move_face` требует вычислить его заранее | + | вставка N мм в призматическую ножку | `split_solid_by_plane` → `move_body` → `move_face` (мост) → `boolean_union` | цепочка проверена end-to-end: проставка 39.45 → 41.45 мм | + + `resize_face` и `replace_face` требуют КОМПАС v25; на версиях старше они отвечают отказом с + номером установленной версии, и тогда остаётся `move_face` с арифметикой. + + **Проверяйте прямое редактирование РАЗНОСТЬЮ ОБЪЁМОВ против аналитической формулы** — объём и так + печатается в сводке каждой операции, лишних вызовов не нужно. Расточка отверстия обязана снять + ровно `π(R²−r²)·h`, наращивание грани — добавить ровно `площадь_грани × Δh`. Считайте формулу по + СВОИМ числам и сверяйте с тем, что напечатала операция: одно число ловит и «операция ушла не + туда», и «отверстие перестало быть сквозным». + + **Оси отверстий сверяют полем `axis`.** У грани типа `cylinder`, `cone`, `torus` `list_faces` + отдаёт `axis` — точку на оси и направление, в мировых координатах; у цилиндра и сферы там же + `radius`. Требование «оси сдвигать нельзя» доказывается сравнением `axis` до и после, а не + арифметикой по расстояниям. Осторожно с двумя соседними полями: `normal` у цилиндра — про + сторону поверхности, а не про ось, а `pointOnFace` лежит на стенке (help(topic="cylinder-axis")). +5. **Перед выдачей:** последняя операция «Построение чистое» → `export_model` (STEP для обмена, + STL для печати). diff --git a/plugin/skills/kompas-3d/reference/parametrization.md b/plugin/skills/kompas-3d/reference/parametrization.md new file mode 100644 index 0000000..579856e --- /dev/null +++ b/plugin/skills/kompas-3d/reference/parametrization.md @@ -0,0 +1,38 @@ +# Параметризация: сначала переменные, потом геометрия + +Дополнение к SKILL.md §«Параметризация». Читай это перед первым эскизом нового документа — план +переменными стоит один вызов на размер, пока деталь пуста, и пересборку после десятка операций. + +**В новом документе первым делом опиши будущую модель переменными — до первого эскиза.** Набор +переменных — это план построения числами и паспорт, по которому человек поймёт модель, не разбирая +дерево. + +- **Заводи переменной то, что было РЕШЕНИЕМ**: габариты из ТЗ, толщины, зазоры, отступы, кегли, + радиусы. Выводимое задавай формулой (`plate_L = badge_L - 2*edge_gap`) — правка ведущего размера + пересчитает зависимые сама. Позиции, подобранные замером, оставляй числами: связь — обещание, + что правка переменной даст осмысленный результат, а на позиционных числах она чаще ломает деталь. +- **Размер передавай выражением прямо в операцию** (`extrude(depth="badge_T")`) — параметр сразу + становится ведомым, внутреннее имя параметра («Расстояние 1») знать не нужно. Переменная должна + существовать ДО операции — потому паспорт и заводят первым. Задним числом и для параметров, + которых нет среди аргументов инструмента (углы уклона, второе направление), — `link_parameter`. +- **Промахи сервер ловит сам:** ссылка на несуществующее имя — отказ с перечнем виновных (и в + выражениях операций, и в `set_variable`; регистр значим), функции в формулах — предупреждение + (тригонометрия в радианах, разделитель аргументов «;», непонятая запись = молчаливый 0). + Твоя часть — **сверить вычисленные значения из ответа со своей прикидкой**. +- **`note` обязателен** и пишется по-русски, со смыслом и единицей; `(ТЗ)` в тексте по-прежнему + полезен человеку, но для машины число из ТЗ метится ПРИЗНАКОМ: + `set_variable([{name:"dish_r", expression:"90", note:"по ТЗ", information:true}])`. Комментарий + вычистка не читает, признак — читает. **Что замерил — верни в переменную** (фактический кегль, + прирост каймы), иначе паспорт разойдётся с моделью и станет дезинформацией. Переосмысливать можно + всё, кроме помеченного `(ТЗ)`. +- **После связывания правка паспорта перестраивает деталь** (проверено: `set_variable(badge_T=4)` → + габарит по Z 3.00 → 4.00, объём 5154.5 → 5861.5 мм³). Это и есть параметрическая модель, + а не комментарий к ней. **До связывания — не перестраивает**, и `set_variable` говорит об этом + прямо: «⚠ НИЧЕГО НЕ ВЕДЁТ». Такой ответ — не успех: число поменялось, деталь нет. Исключение одно — + переменная и не должна ничего вести (справка из ТЗ): пометь её `information=true`, и пометка уйдёт + вместе с риском вычистки. +- **Сверка и уборка в конце:** `describe_model(sections=variables)` помечает «⚠ ничего не ведёт» — это либо забытая + связь, либо справка, которую забыли пометить, либо мусор; разбери все три. + `delete_variable(unused=true)` не трогает справочные (`information`) и внешние (`external`) — + остальное сносит без разбора. Значит порядок такой: `link_parameter` на всё, что должно вести + геометрию, `set_variable(information=true)` на всё, что остаётся справкой, и только потом уборка. diff --git a/plugin/skills/kompas-3d/reference/text-and-relief.md b/plugin/skills/kompas-3d/reference/text-and-relief.md index 13e92cf..fccf839 100644 --- a/plugin/skills/kompas-3d/reference/text-and-relief.md +++ b/plugin/skills/kompas-3d/reference/text-and-relief.md @@ -1,10 +1,65 @@ # Надписи и рельеф — точная подгонка и отделка -Дополнение к разделу «Надписи, логотипы и рельеф» SKILL.md. Здесь — замеры и приёмы для точной -компоновки надписи (подгонка кегля и ширины, поведение каймы, стыковка блоков) и отделки рельефа. -Все числа получены пробами на конкретных начертаниях (Lobster, Bevan, Zilla Slab): это порядок -величины и форма зависимости, а не константы — свои значения перезамеряй на своём шрифте, -`widthFactor` и `thinThickness`. +Дополнение к разделу «Надписи, логотипы и рельеф» SKILL.md: выбор маршрута для внешнего контура, +перевод SVG-логотипа в примитивы эскиза, точная подгонка надписи (кегль, ширина, кайма, стыковка +блоков) и отделка рельефа. Все числа получены пробами на конкретных начертаниях (Lobster, Bevan, +Zilla Slab): это порядок величины и форма зависимости, а не константы — свои значения перезамеряй +на своём шрифте, `widthFactor` и `thinThickness`. + +## Внешний контур: не диктуй координаты, если фигуру нельзя описать формулой + +Три маршрута, и выбор между ними механический — не по вкусу, а по происхождению геометрии. + +| Откуда фигура | Чем строить | +|---|---| +| **Вычисляется**: пластины, рёбра, сетка отверстий | `sketch_create(entities[])` — прямо в вызове, размеры вяжи переменными | +| **Срисована**: логотип, шаблон, кулачок, профиль | внешний вектор → JSON → `sketch_create(entitiesFile=…)` | +| **Пришла в CAD-формате**: файл от смежника | `import_drawing` (.dxf/.dwg) → фрагмент → `fragment_place` | + +**Обводка занимает сотни примитивов, и переписывать их в тело вызова незачем.** У `sketch_create`, +`sketch_add` и `fragment_create` есть `entitiesFile` — путь к JSON того же формата, что `entities`; +сервер читает файл сам. Замер на эмблеме Volvo: SVG в 1.5 КБ даёт 96 примитивов (50 отрезков + +46 дуг) и 9.5 КБ JSON; через `entitiesFile` это один вызов на одну строку аргументов, а эскиз +строится за один заход. Оба параметра можно задать вместе — сначала `entities`, следом файл: +так к готовому контуру дописывают рамку или ось, не трогая файл. + +Что проверить **до** постройки, а не после: габарит в ответе (`fragment_create` печатает +фактический — сверь с задуманным) и вложенные контуры. Правил заливки у эскиза КОМПАС нет: любой +вложенный контур он режет как отверстие, даже там, где вектор заливал материал. Конвертер такие +места называет заранее — на них смотри до выдавливания, иначе дыры обнаружатся на снимке. + +## Логотип, которого нет в шрифте: SVG → примитивы эскиза + +Переводи готовый вектор в примитивы эскиза сам — `line` и `arc3points`, а не ломаной: контур из +сотен звеньев даёт деталь в сотни граней, и фаска по такому низу не строится. Кубические Безье режь +пополам, пока дуга по трём точкам не ляжет в допуск. Четыре вещи, каждая из которых стоила захода: + +- **Ось Y в SVG смотрит вниз** — без инверсии контур встаёт вверх ногами, и это видно только на снимке. +- **Дуги задавай тремя точками** (`arc3points`): концы заданы явно, соседние примитивы стыкуются + точно. Перед постройкой проверь цепочку — конец примитива против начала следующего, разрыв 0. +- **Чисти мелочь под масштаб детали — параметром `minSegment`.** SVG рисуют в своём габарите (сотня + условных единиц), и его скругления после масштабирования превращаются в дуги по 0.02–0.5 мм; + замыкающий `z` часто даёт ещё и сегмент нулевой длины. `chamfer_edge` по такому набору отвечает + «катет больше длины N из M рёбер» — и это правда. Передай `sketch_create(minSegment=катет)` (тот же + параметр есть у `sketch_update` и `fragment_create`), и та же фаска пройдёт с первой попытки; + отчёт о выброшенном читай, а не пролистывай — `help(topic="contour-cleanup")`. + +**Порядок для фаски по контуру логотипа — только такой, и он неочевиден:** + +1. **чистка** порогом ≈ будущего катета (`minSegment`); +2. **замер ЧИЩЕНОГО силуэта** — `sketch_measure_thickness(entitiesFile, minSegment=тот же порог)`; +3. **катет по узкому месту** чищеного силуэта: `c ≤ (узкое_место − 2·w)/2`, где `w` — ширина нити. + +Менять шаги местами нельзя: **чистка СУЖАЕТ узкое место**. Она заменяет скругление острым углом — +в выпуклом углу это добавляет материал (габарит подрастает), а в вогнутом срезает его, и именно +вогнутые углы образуют перемычки между штрихами. Сужение доходит до единиц процентов узкого места — +этого хватает, чтобы катет, подобранный по нечищеному контуру, перестал проходить свой же критерий. +Числа снимай на СВОЁМ контуре: они принадлежат рисунку, а не приёму. + +- **Ширину штриха и узкое место меряй инструментом, а не глазом** — `sketch_measure_thickness` + ничего не строит и документа не требует, поэтому зовётся ДО эскиза + (`help(topic="silhouette-thickness")`). От этих чисел зависит, останется ли что-то от рисунка + после фаски и напечатается ли он вообще; шаг растра в ответе — точность округления катета. ## Подгонка ширины и кегля @@ -61,7 +116,11 @@ - **Толщина каймы — ещё и параметр стыковки блоков.** Кайма опускает силуэт надписи ниже её глифов на `t`, и именно это перекрытие с плашкой держит деталь одним телом. Считай так: низ каймы = низ глифов − t, и он должен зайти на плашку хотя бы на несколько десятых. Проверка — - сводка «Тел:» в ответе самого `primitive(union)`. + сводка «Тел:» в ответе самого `primitive(union)`: у примитивов она надёжна, потому что сервер + сам сверяет объём до и после и отвергает объединение, ничего не давшее. **У `loft`/`sweep` этой + сводке верить нельзя** — там она однажды напечатала «Тел: 1», когда тел было два (см. основной + навык, п. «Ответ мутирующей операции»); в сомнительном случае считай тела + `describe_model(sections="bodies")`. - **Кайма вокруг ПРЯМОУГОЛЬНОЙ плашки — вторым примитивом, а не тонкой стенкой.** Стенка обходит контуры эскиза, а плашка по playbook'у строится `primitive`, и каймы у неё не появится. Клади под неё второй блок на `2·t` больше и на высоту подложки (85 × 13.5 × 2 под плашкой 82 × 10.5 × 3) — @@ -89,16 +148,15 @@ до нужной части силуэта помогает `minY`/`maxY`). **Порядок «сначала скругления углов, потом фаска» работает** — дуги от R0.5 фаске не мешают. В отборе встречается тип `other` (у сложных кривых) — он тоже принимается параметром `type`. -- **Отказ фаски на НЕЧИЩЕНОМ контуре — не повод уменьшать катет: попробуй УВЕЛИЧИТЬ.** Успех - немонотонен по размеру. На контуре логотипа (54 ребра низа, острые клинья между элементами) - `chamfer_edge` отказывает при 0.4, 0.5, 0.6 и 0.7 и проходит при **0.8** — проверено подряд, - с `rebuild` между попытками и на неизменных индексах рёбер. Похоже, малый катет оставляет в остром - клине вырожденный «нос», который КОМПАС сшить не может, а большой срезает остриё целиком. - **Но это верно ТОЛЬКО пока контур не почищен**, и полагаться на такой подбор не нужно: правильное - лечение — `minSegment` (см. SKILL.md §«Надписи, логотипы и рельеф»), а не перебор катета. - Замер на том же логотипе: почищенный порогом 0.5 контур (34 примитива) фаску **0.5 принимает**, - а эталонные **0.8 отвергает** — рабочий диапазон катета сдвигается ВНИЗ вместе с узким местом - (1.98 → 1.82 мм). После чистки совет «попробуй увеличить» уже вреден. +- **Отказ фаски на НЕЧИЩЕНОМ контуре — не повод уменьшать катет: успех немонотонен по размеру.** + На контуре с острыми клиньями между элементами `chamfer_edge` отказывает на нескольких катетах + подряд и проходит на большем — проверено подряд, с `rebuild` между попытками и на неизменных + индексах рёбер. Похоже, малый катет оставляет в остром клине вырожденный «нос», который КОМПАС + сшить не может, а большой срезает остриё целиком. **Но перебирать катет не надо**: правильное + лечение — `minSegment` (см. SKILL.md §«Надписи, логотипы и рельеф»). После чистки совет «попробуй + увеличить» становится вреден: рабочий диапазон катета сдвигается ВНИЗ вместе с узким местом, и + катет, проходивший на нечищеном контуре, чищеный отвергает. Не переносите подобранное значение + между состояниями контура — оно к ним привязано. Прежняя формулировка добавляла «исключение самых коротких рёбер картину не меняет, то есть дело не в них» — вывод неверен: исключённое из `edgeIndices` ребро остаётся в теле, и это не то же самое, что убрать сегмент из контура. Убранный из контура — меняет. diff --git a/plugin/skills/kompas-fdm-design/SKILL.md b/plugin/skills/kompas-fdm-design/SKILL.md index c2ba55e..aadbde6 100644 --- a/plugin/skills/kompas-fdm-design/SKILL.md +++ b/plugin/skills/kompas-fdm-design/SKILL.md @@ -51,8 +51,13 @@ description: > ## Два правила (соблюдай всегда) **1. Ориентация печати — первое проектное решение.** -- **Спроси у пользователя** (если не задано): главное направление рабочей нагрузки и - косметические/критичные грани. **Путь нагрузки из геометрии не выводится** — его задаёт задача. +- **Путь нагрузки и косметические грани из геометрии не выводятся** — их задаёт задача. Спроси + пользователя, **если есть кого спрашивать**; работаешь автономно (субагент, пакетный прогон) — + выведи из постановки: что деталь держит, куда смотрит рабочая поверхность, чем она крепится. + Выведенное — **запиши допущением** в ответ («принято: нагрузка — вес камеры 150 г вниз на плече + 53 мм; косметика не задана»), чтобы человек увидел, что именно ты предположил. Молчаливое + «спросить некого» — не повод пропустить решение об ориентации: без него все дальнейшие правила + повисают. - Реши постановку на стол (ось Z = рост слоёв). От неё зависит: где нависания; куда смотрят отверстия (вертикальные → компенсация Ø; горизонтальные → teardrop); путь нагрузки (**держи в XY**; Z-сжатие можно, Z-растяжение/срез — нет); плоскости сопряжения (на XY-гранях); «лесенка» на @@ -65,47 +70,151 @@ description: > **FDM-чек-лист** (деталь *печатнопригодна*) — разные проверки. **Гео-аудит эвристический и не доказывает печатнопригодность** (не ловит путь нагрузки/анизотропию). +## Ориентация: когда три требования не сходятся + +У консольной детали (кронштейн, полка, держатель) требования правила 1 — **нагрузка в XY**, +**нависания ≤ `θ_max`**, **опорная площадка** — одновременно невыполнимы. Постоянного приоритета у +них нет: он решается ЧИСЛОМ, и число считается за две минуты. + +**Прикидка «прочность против ориентации».** Напряжение в корневом сечении против межслойной +прочности — то есть против той самой величины, ради которой требуют «нагрузку в XY»: + +1. `M = F·L` — момент в корне: `F = m·g` (Н), `L` — плечо до корневого сечения (мм) → Н·мм. +2. `W = b·h²/6` — момент сопротивления прямоугольного сечения (`h` — размер В ПЛОСКОСТИ изгиба), мм³. + Размеры сечения бери `describe_model(sections="box")` или `measure` по граням, а не из головы. +3. `σ = M/W` (Н/мм² = МПа). +4. Порог — межслойная прочность: доля от XY-прочности по §6 `fdm-rules.md`, для **PLA ~20–30 МПа** + (PETG ~15–25, ABS ~8–15). + +**Точность прикидки — ПОРЯДОК, и в узкой полосе этого мало.** `W = b·h²/6` — это сплошное сечение, +а печатная деталь сплошной не бывает: тот же навык требует стенки `n·w` и оставляет заполнение +по умолчанию, так что настоящий момент сопротивления корневого сечения заметно ниже габаритного +(у оболочки в три периметра на сечении 34×24 — примерно вдвое). Пока запас идёт порядками, это +не мешает; в полосе **<3×**, где число и решает, погрешность метода равна ширине полосы. Поэтому: +при запасе <3× пересчитайте `W` по фактическому сечению (оболочка `n·w` + заполнение) либо просто +возьмите `W` вдвое меньше габаритного и посмотрите, в какой строке таблицы окажетесь. + +| Запас `σ_меж/σ` | Что делать | +|---|---| +| **< 3×** | «Нагрузка в XY» — жёсткое требование, ориентацию выбирает оно; нависания лечим поддержками, площадку — фланцем, вплоть до разбиения детали (§13) | +| **3–10×** | Держать XY, если это не стоит поддержек на функциональных гранях; несущее сечение поднять (§6: ×2 при неизбежной Z-нагрузке) | +| **≥ 10×** | Требование СНИМАЕТСЯ — «слои на отрыв» здесь не размерный случай. Ориентацию выбирай по печати: сначала нависания, затем опорная площадка и устойчивость | + +**Замер (кронштейн камеры):** 150 г на плече 53 мм → `M = 0.150·9.81·53 ≈ 78 Н·мм`; корневое сечение +34×24 → `W = 34·24²/6 ≈ 3.3·10³ мм³`; `σ ≈ 24 кПа` против ~30 МПа межслойной PLA — запас **три +порядка**. Слои здесь не решают ничего, и постановка выбрана по нависаниям, а не по прочности. +Без этой прикидки правило «нагрузка в XY» читается как запрет и уводит в худшую по печати ориентацию. + +**Постановки перебирают по граням габарита**, их немного: у плоско-изогнутой детали — две «плашмя» +(широкой гранью вниз, посадочной гранью вниз) и одна «стоя» (корневым торцом вниз). Выпиши для +каждой три числа — площадь нависающих граней, площадь опорного пятна и `σ` — и выбор перестанет +быть спором вкусов. «Стоя» обычно выигрывает по нависаниям и проигрывает по устойчивости; её +проверяют отдельно (гео-аудит, проверка центра масс). + ## Рабочий цикл 1. **Калибровка**: сопло→`w`; слой→`h`→`θ_max`; материал→поправки. -2. **Ориентация** (правило 1): опрос (нагрузка/косметика) → постановка, ось слоёв, сопряжения, - «лесенка», поддержки. +2. **Ориентация** (правило 1 + §«когда три требования не сходятся»): нагрузка/косметика — опросом + или допущением → перебор постановок с числами → ось слоёв, сопряжения, «лесенка», поддержки. 3. **Правила эскиза/операции** (строй через `kompas-3d`): стенки `n·w`; нависания → скос под - `θ_max`; горизонтальные отверстия → teardrop; вертикальные → компенсация Ø; фаска у основания; + `θ_max`; горизонтальные отверстия → teardrop (полная капля; усечённая касательная, если клюв + мешает виду; мягкий колпачок, если по отверстию идёт посадка — выбор и формулы в + `references/fdm-rules.md` §3), причём у МЕЛКОГО отверстия форму выбирают по ПРОЛЁТУ крыши, + а не по углу боковин; вертикальные → компенсация Ø; фаска у основания; зазоры посадок (со знаком); заходные фаски; мин. элементы/текст; бобышки/инсёрты/защёлки. Числа — в `references/fdm-rules.md`. 4. **Гео-аудит** (`references/geometry-audit.md`) — инструментами осмотра. -5. **Предусловия экспорта**: единое тело/манифолд (`boolean_union` при необходимости) → +5. **Предусловия выдачи**: единое тело/манифолд (`boolean_union` при необходимости) → построение чистое. -6. **Чек-лист** → экспорт **через `export_model`**: слайсеру нужен `.stl` - (`export_model(path="…​.stl")` — двоичная сетка), а `.step` остаётся для передачи модели в САПР. +6. **Чек-лист** → выдача. Обменный файл кладите **только если о нём попросили**: модель самоценна, + а нарезает её человек сам. Попросили — `export_model` (`.stl` — двоичная сетка для слайсера, + `.step` — для передачи модели в другую САПР). ## Чек-лист печатнопригодности -- [ ] Направление нагрузки и косметические грани **получены от пользователя**; ориентация - зафиксирована; нагрузка в XY (или Z только на сжатие); сопряжения на XY-гранях. +- [ ] Направление нагрузки и косметические грани **получены от пользователя или выведены из + постановки и записаны допущением**; ориентация выбрана числом (запас `σ_меж/σ`), а не по + умолчанию; нагрузка в XY (или Z только на сжатие, или запас ≥10×); сопряжения на XY-гранях. - [ ] Стенки кратны `w` (≥2·w; несущие ≥3·w, т.е. 3 периметра); нет «не кратных `w`» (кроме функциональных). -- [ ] Нет 90°-полок; нависания ≤`θ_max` или заменены скосами; мосты в пределах пролёта по короткой - стороне (числа — `references/fdm-rules.md`); внутренним поддержкам — доступ. -- [ ] Горизонтальные отверстия — teardrop/D (геометрия из `θ_max`); вертикальные — компенсация Ø; - глухие — дно ≥2–3 мм. -- [ ] Фаска у основания (elephant foot); внутренние углы ≥R0.5; опорная площадка есть. +- [ ] Нет 90°-полок; нависания ≤`θ_max` или заменены скосами — отбор ровно в той форме, что в + §«Гео-аудит»: `list_faces(normal="0,0,-1", normalTolerance=90−θ_max, minZ=)` + (дефолтный допуск 15° ловит только почти горизонтальные потолки, а без `minZ` в улов + попадает опорное пятно, без `minArea` — микрогрань, которая нависанием быть не может); + пролёт каждого моста **замерен** `measure`, а не прикинут, и + укладывается в `references/fdm-rules.md` §2 по короткой стороне; внутренним поддержкам — доступ. +- [ ] Горизонтальные отверстия — teardrop/D (геометрия из `θ_max`), усечённая касательная там, где + клюв мешает виду, либо мягкий колпачок там, где отверстие несёт посадку, и тогда компромисс + назван вслух; у формы положе `θ_max` пролёт крыши посчитан и укладывается в норму моста; + вертикальные — компенсация Ø; глухие — дно ≥2–3 мм. +- [ ] Фаска у основания (elephant foot); внутренние углы ≥R0.5; опорная площадка есть, и для + высокой детали проверена устойчивость по §«Гео-аудит»: очертание пятна — + `list_edges(minZ=, maxZ=)`, проекция ЦМ + (`describe_model(sections="mass")`) внутри этого прямоугольника, высота ЦМ над столом + ≲3× полуширины пятна в узком направлении. - [ ] Посадки по таблице со **знаком** (натяг — вычесть); допуск ±0.2 **НЕ** прибавлен к зазору; заходные фаски на сопряжениях. -- [ ] Мин. элементы/текст ≥ порогов; аспект тонких выступов ≤4–5×. +- [ ] Мин. элементы/текст ≥ порогов (у надписи проверен ШТРИХ, а не кегль: чертёжный шрифт даёт + `h/14` и порога не берёт — `references/fdm-rules.md` §7); сторона грани под надпись выбрана + так, чтобы она не вышла зеркальной (§10); аспект тонких выступов ≤4–5×. - [ ] Бобышки/инсёрты (бор отверстия ≤ OD инсёрта, ставить с Z-грани)/резьба/защёлки (изгиб в XY) по правилам. - [ ] Полости — дренаж/вент; критичные поверхности не под «лесенкой»/поддержкой. - [ ] Предусловия экспорта: единое тело/манифолд; построение чистое. ## Гео-аудит (кратко) -Лёгкая самопроверка построенной модели **существующими** инструментами осмотра MCP: -`describe_model` / `list_faces` / `list_faces(index=N)` (нависания по нормалям нижних граней; цилиндры с -горизонтальной осью → нужен teardrop), `describe_model(sections="box")` (как ось слоёв соотносится с габаритом), -`measure` (номиналы/зазоры), `describe_model(sections="bodies")` + чистое построение (единое тело). **Границы и методика — -`references/geometry-audit.md`.** Аудит эвристический; истинная мин. толщина стенки и полный детект -криволинейных нависаний не решаются — это **не приговор и не доказательство печатнопригодности**. +Лёгкая самопроверка построенной модели **существующими** инструментами осмотра MCP — вызовами, +а не пожеланиями: + +- **Нависания одним отбором:** `list_faces(normal="0,0,-1", normalTolerance=90−θ_max, + minZ=)` — всё вернувшееся кандидаты в нависания, и тем хуже, чем ближе нормаль + к чистому −Z (горизонтальный потолок). При дефолтном `θ_max`=45° допуск равен 45 — это совпадение + чисел, а не тождество: для PETG с `θ_max`=40° допуск **50**. + **`minZ` тут не украшение, а условие правильности.** Отбор по нормали ЗНАКОВЫЙ, а у грани, + лежащей на столе, внешняя нормаль равна ровно (0, 0, −1) — она проходит при ЛЮБОМ допуске и + выглядит САМЫМ страшным нависанием, хотя это опорное пятно. Замер на кронштейне: тот же отбор + без `minZ` (допуск 45°) вернул девять граней, и наибольшая из них — 515.44 мм² с центром ровно + на `Zmin` (−50.50) — была пятном первого слоя. Первый слой печатается по столу и нависанием не + бывает: отсекайте его окном, `Zmin` берите из `describe_model(sections="box")`. + **`minArea` — вторая половина того же отбора.** Угол наклона грани — критерий для ПРОТЯЖЁННОГО + нависания; на грани в единицы мм² он не значит ничего, потому что перекрыть надо не угол, а + пролёт. Замер: крыша отверстия Ø6, срезанная касательными под 22.5° к горизонтали, приходит в + улов как нарушение (нависание 67.5° при пороге 45°) — при площади 2.84 мм² и пролёте 2.30 мм, + который печатается заведомо. Ставьте `minArea` порядка 5–10 мм², а всё мелкое, что всё же + хотите проверить, судите пролётом (`measure`), а не углом: иначе аудит гонит на редизайн там, + где всё в порядке. + Дальше `list_faces(index=N)` по подозрительным; цилиндр с горизонтальной осью → нужен teardrop. +- **Пролёт моста меряют, а не считают по эскизу:** `measure(kind1="face", index1=…, kind2="face", + index2=…)` между ВНУТРЕННИМИ гранями опор (замер: 49.0 мм между стойками). Сравнивать с §2 + `fdm-rules.md`: 49 мм при потолке PLA 15–25 мм — это редизайн, а не «как-нибудь напечатается». +- **Устойчивость высокой печати** — самая дешёвая проверка из всех и единственная, которой нет + больше нигде, но стоит она ТРЁХ вызовов, а не одного, и площади пятна для неё НЕ хватает: + `list_faces` отдаёт по грани площадь, нормаль и центр — ни границ, ни полуширины из них не + выводится. Очертание пятна даёт `list_edges`: он печатает координаты концов рёбер, а окно по Z + оставляет только рёбра первого слоя. + 1. `describe_model(sections="box")` → `Zmin`. + 2. `describe_model(sections="mass")` → центр масс в мировых координатах (даром). + 3. `list_edges(minZ=, maxZ=)` → концы рёбер пятна; их разброс по X и по Y и + есть прямоугольник пятна, а половина меньшей стороны — полуширина в узком направлении. + У КРУГЛОГО пятна так не выйдет: у замкнутой окружности концы совпадают, разброс схлопывается + в точку. Тогда полуширина — радиус, и берут его `list_faces(index=N)` (у грани печатается + радиус) или из длины ребра (`πD`). + + Проверок две: проекция ЦМ `(x, y)` обязана лежать **внутри** этого прямоугольника (у консольной + детали «стоя» она легко уходит за него — деталь падает недопечатанной), и высота ЦМ **над столом** + (`z_ЦМ − Zmin`) ≲3× полуширины (эвристика; выше — интегральный фланец §5 или разбиение §13). + **Замер (кронштейн «стоя»):** габарит `Z −50.50…180.00`, ЦМ `(0.00, 4.93, 56.61)`; + `list_edges(minZ=−51, maxZ=−50)` вернул четыре ребра — `X −50.50…50.50`, `Y 0.00…8.00`, то есть + пятно 101 × 8 мм. Проекция ЦМ (0.00, 4.93) внутри прямоугольника — первая проверка пройдена; + высота ЦМ над столом 56.61 − (−50.50) = **107.1 мм** при полуширине узкого направления **4 мм**, + это **27×** при пороге 3× — вторая провалена. Сработало именно второе число, и без очертания + пятна взять его было неоткуда. +- **Тело, габарит, номиналы:** `describe_model(sections="bodies")` + чистое построение (единое + тело), `describe_model(sections="box")` (как ось слоёв соотносится с габаритом), `measure`. + +**Границы и методика — `references/geometry-audit.md`.** Аудит эвристический; истинная мин. толщина +стенки и полный детект криволинейных нависаний не решаются — это **не приговор и не доказательство +печатнопригодности**. **Просвет канала и толщину перемычки спрашивают, а не считают.** `measure(kind1="body", index1=0, kind2="body", index2=1)` даёт минимальное расстояние между телами целиком — не нужно угадывать, какие diff --git a/plugin/skills/kompas-fdm-design/references/fdm-rules.md b/plugin/skills/kompas-fdm-design/references/fdm-rules.md index 523a967..bb3f98a 100644 --- a/plugin/skills/kompas-fdm-design/references/fdm-rules.md +++ b/plugin/skills/kompas-fdm-design/references/fdm-rules.md @@ -38,10 +38,46 @@ критичные — рассверливать. - **Горизонтальные (ось в XY)** → **teardrop** или **D-отверстие** (плоский верх). Мин. Ø **2 мм**. Круглая часть тоже печатается уже → **+0.1–0.2 мм** к её Ø. -- **Геометрия teardrop:** боковины касательны окружности под углом `θ_max` к вертикали (с двух - сторон), сходятся в вершине на вертикальной оси. Высота вершины над центром = **`r / sin θ_max`**; - включённый угол при вершине = **`2·θ_max`**. При `θ_max`=45° → `r/sin45° = √2·r ≈ 1.414·r` над - центром (= **0.414·r над верхом окружности**), угол 90°. Низ — оставшаяся дуга окружности. +- **Геометрия teardrop (полная капля):** боковины касательны окружности под углом `θ_max` к вертикали + (с двух сторон), сходятся в вершине на вертикальной оси. Высота вершины над центром = + **`r / sin θ_max`**; включённый угол при вершине = **`2·θ_max`**. При `θ_max`=45° → + `r/sin45° = √2·r ≈ 1.414·r` над центром (= **0.414·r над верхом окружности**), угол 90°. + Низ — оставшаяся дуга окружности. Это единственная форма, у которой ВСЯ крыша самонесущая по + критерию `θ_max`; платой идёт максимальное искажение круга (при `θ_max`=45° некруглыми становятся + 90° дуги из 360°). +- **Усечённая касательная капля — когда клюв мешает глазу.** Полная капля при `θ_max`=45° выступает + на 0.414·r, и на мелком отверстии это заметно. Та же конструкция, но с точкой касания на `±φ` от + макушки при `φ < θ_max`: боковины остаются КАСАТЕЛЬНЫМИ (контур гладкий, излома на стыке с дугой + нет, круг не срезается вовсе), а + - вершина над центром = **`r / cos φ`**, то есть выступ над окружностью = **`r·(1/cos φ − 1)`**; + - боковины наклонены к вертикали на **`90° − φ`** — тем положе, чем короче клюв; + - перекрываемый пролёт = **`2r·sin φ`**. + **Судить эту форму по углу боковин бесполезно — судите по пролёту.** У маленького отверстия + `2r·sin φ` укладывается в единицы миллиметров, а мост такой длины FDM печатает не задумываясь: + `θ_max` — критерий для ПРОТЯЖЁННЫХ нависаний, а не для перемычки в пару миллиметров. + Замер (Ø6, `φ`=22.5°): выступ **0.247 мм** против 1.243 у полной капли, пролёт **2.30 мм**, + боковины 67.5° от вертикали — печатается без поддержек, крыша отверстия чистая. + +- **Мягкий колпачок — когда круглость важнее, чем полная самонесущесть.** Посадка вала, подшипник, + штифт: полная капля съедает верхнюю четверть окружности, и сопряжение по ней уже не работает. + Тогда заменяют не сектор `±(90−θ_max)`, а УЗКИЙ сектор `±φ` у самой макушки, подняв вершину на + `k` слоёв (`k·h`) над окружностью: + - точки стыка — на `±φ` от макушки, вершина — на оси, на `r + k·h` над центром; + - включённый угол при вершине = **`2·arctan( r·sin φ / (r + k·h − r·cos φ) )`**; + - **угол остаётся ТУПЫМ, пока `r·(sin φ + cos φ − 1) > k·h`** — это и есть связь между шириной + сектора и подъёмом: ужимая `φ`, синхронно снижайте `k`, иначе колпачок вырождается в шпиль. + - Замер (Ø8, `φ`=12°, `k`=2 при `h`=0.2, т.е. подъём 0.4 мм): круглыми остаются **336° из 360°**, + стык отходит от оси на **±0.83 мм**, угол при вершине **119°**, боковины — 59.6° от вертикали. + - **Честная граница приёма:** у чистой окружности зона нависания — сектор `±(90−θ_max)` от макушки + (при 45° это ±45°, ширина `2r·sin45°`≈1.41·r). Колпачок `±12°` снимает только самую макушку — + участок от 12° до 45° остаётся нависанием. То есть это НЕ решение по `θ_max`, а замена плоского + потолка на микро-мост шириной ~`2r·sin φ` за `k` слоёв. Берите его осознанно: круглость в обмен + на частичное улучшение печати, а не на полное. +- **Точки касания задавайте с ПОЛНОЙ точностью — округление рвёт контур.** Дуга плюс две касательные + замыкаются у КОМПАС по допуску жёстче обычного округления: те же координаты, срезанные до 6 знаков + (−16.668356 вместо −16.6683532), дали «код 19: контур должен быть замкнут», и вырез развалился. + Коварство в том, что `sketch_update` отчитался габаритом как об успехе — невалидность вылезла + только предупреждением «2 операц. в ошибке». Считайте `r·sin φ`/`r·cos φ` и подставляйте все знаки. - **Глухое отверстие:** дно = внутренний мост → **толщина дна ≥2–3 мм** или купольное/ вентилируемое. Сквозные предпочтительнее. - **Отступ от края** — через остаточную перемычку: стенка между отверстием и краем **≥2–3·w** @@ -73,6 +109,9 @@ ## 6. Ориентация и прочность - **Z (межслойная) прочность от XY:** PLA ~40–55%, PETG ~35–50%, **ABS ~20–35% (выброс)**. Несущую нагрузку — в **XY (вдоль слоёв)**. +- **В абсолютных числах** (нужны, чтобы посчитать запас — SKILL.md §«Ориентация: когда три + требования не сходятся»): XY на разрыв PLA ~50–60 МПа, PETG ~45–50, ABS ~35–40 ⇒ межслойная + **PLA ~20–30, PETG ~15–25, ABS ~8–15 МПа**. Порядок величины, не паспорт конкретного филамента. - **Z-сжатие допустимо** (слои в сжатии не расслаиваются); избегать **Z-растяжения и Z-среза**. - Изгиб: слои в растяжении/сжатии, не на срез по линии слоя. - Z-нагрузка неизбежна → **увеличить несущее сечение** ~×2 относительно XY-расчёта. @@ -84,6 +123,13 @@ - **Выпуклый** текст: штрих **≥0.5 мм**, высота **≥2·h (~0.4 мм)**, sans-serif bold. - **Гравированный** текст: штрих **≥2·w (~0.8–0.9 мм)** (нужно ≥2 периметра; 0.6 мм не влезает), глубина **≥2·h (~0.4 мм)**. (pt не используем — геометрия в мм.) +- **Штрих задаёт ШРИФТ, а не кегль, и чертёжные шрифты под печать не годятся.** По ГОСТ 2.304 + толщина линии чертёжного шрифта равна `h/14`: кегль 8 даёт 0.57 мм — полторы ширины сопла, то + есть ниже порога 2·w. Такой шрифт берёт порог только с кегля ~13 мм, а уверенно читаемые 3·w — + с ~18 мм. У жирного гротеска штрих ≈ `h/4.5` (Arial Black при кегле 10 → ~2.2 мм), и порог + выполняется с запасом уже на кегле 4–5 мм. Замер: «2» кеглем 8 шрифтом GOST type A на + вертикальной стенке не читается вовсе; тот же символ кеглем 10 шрифтом Arial Black читается + издалека. Маркировку набирайте жирным sans-serif и проверяйте штрих, а не высоту. ## 8. «Лесенка» (staircase) — критерий ориентации - Наклонные/криволинейные поверхности дают ступени: глубина ≈ **`h / tan(α)`** (α — угол от @@ -107,6 +153,13 @@ верхнего слоя (там видны швы и «лесенка»). Тогда деталь и моделируют в этой ориентации — лицом к XOY, — а несимметричный рисунок (логотип, текст) **зеркалят**: на лицевую грань смотрят со стороны −Z. Проверка — снимок со стороны лица (`model_snapshot(view=rear)` для эскиза на XOY). +- **Зеркалит не только лицо на столе — зеркалит ЛЮБАЯ грань, обращённая против нормали эскиза.** + Надпись читается с той стороны, куда смотрит нормаль плоскости: XOY → **+Z**, XOZ → **+Y**, + YOZ → **−X** (нормаль YOZ уходит в МИНУС X — тот же замер, что разворачивает на ней выдавливание). + Значит гравировка по эскизу на YOZ, положенная на щеку с бОльшим X, выйдет зеркальной, а на + щеку X=0 — правильной. Замер: цифра на щеке X=20.4 читалась зеркально при ЛЮБОМ `angle`; тот же + эскиз, перенесённый на X=0, встал верно. **Поворотом это не лечится** — `angle` вращает, а не + отражает; лечится выбором грани (или зеркальным контуром). - **Фаска по лицевому контуру — это ещё и размер первого слоя.** При 45° она сжимает силуэт лица на катет `c` по всему периметру, поэтому её потолок задаёт не эстетика, а то, что останется: `ширина_штриха − 2c ≥ 2·w`. У широкого рисунка (штрих ~10 мм) ограничением становится толщина @@ -118,24 +171,19 @@ вписанную окружность, узкое место с координатами и таблицу «офсет `c` → на сколько кусков распалось лицо» — ничего не строя. - **Узкое место зависит от СОСТОЯНИЯ контура, поэтому порядок такой: чистка → замер чищеного → - катет.** Замеры на обводке логотипа шириной 105 мм (штрих по наибольшей вписанной 11.5 мм): - - | контур | узкое место | лицо распадается при | - |---|---|---| - | исходный, 54 примитива | 1.98 мм | c = 0.99 | - | чищеный порогом 0.5, 34 примитива | **1.82 мм** | **c = 0.91** | - - Чистка (`minSegment`) заменяет скругления острыми углами: в выпуклом углу это добавляет материал, - в вогнутом — срезает, а перемычки между штрихами образованы как раз вогнутыми углами. То есть - операция, которую делают РАДИ фаски, уменьшает допустимый катет. Мерьте после неё. -- **Норматив катета — `c = stroke_min/2 − w`**, где `stroke_min` — узкое место, `w` — ширина нити. - Он и есть решение неравенства `ширина − 2c ≥ 2w`. На том же логотипе: `1.82/2 − 0.4 = 0.51`, - и в узком месте остаётся ровно 0.80 мм — два прохода сопла. - **Осторожно с «эталонными» значениями из прошлых работ:** принятая когда-то на этом логотипе - фаска 0.8 собственному правилу НЕ удовлетворяет (`1.82 − 1.6 = 0.22 мм` против нужных 0.8; даже - по нечищеному контуру `1.98 − 1.6 = 0.38`). Она оставляет в узком месте одну нить экструзии — - тело там сплошное, рвётся косметика лицевой грани, а это как раз та поверхность, ради которой - деталь и кладут лицом на стол. + катет.** Чистка (`minSegment`) заменяет скругления острыми углами: в выпуклом углу это добавляет + материал, в вогнутом — срезает, а перемычки между штрихами образованы как раз вогнутыми углами. + То есть операция, которую делают РАДИ фаски, уменьшает допустимый катет — на обводке логотипа это + единицы процентов, и хватает, чтобы катет перестал проходить свой же критерий. Мерьте после неё, + тем же порогом, каким будете строить. +- **Норматив катета — `c = stroke_min/2 − w`**, где `stroke_min` — узкое место ЧИЩЕНОГО силуэта, + `w` — ширина нити. Он и есть решение неравенства `ширина − 2c ≥ 2w`: в узком месте остаётся + ровно `2w`, два прохода сопла. Считайте его на своём контуре — величина принадлежит рисунку. + **Осторожно с «эталонными» значениями из прошлых работ:** катет, принятый когда-то на похожем + знаке, своему же правилу обычно не удовлетворяет — он подбирался под другой рисунок, другой порог + чистки или вовсе на глаз. Катет вдвое сверх норматива оставляет в узком месте одну нить экструзии: + тело там сплошное, рвётся косметика лицевой грани — та самая поверхность, ради которой деталь и + кладут лицом на стол. Сверьте нормативом, а не воспоминанием. - **Верхние рёбра — скругление** (R0.5–2.0); **но** радиус **> ~½ толщины стенки** сам даёт нависание > `θ_max` → тогда фаска/ступень. - **Внутренние углы — всегда скругление ≥R0.5**. diff --git a/plugin/skills/kompas-fdm-design/references/geometry-audit.md b/plugin/skills/kompas-fdm-design/references/geometry-audit.md index 1a63497..8c27323 100644 --- a/plugin/skills/kompas-fdm-design/references/geometry-audit.md +++ b/plugin/skills/kompas-fdm-design/references/geometry-audit.md @@ -7,18 +7,45 @@ | Проверка | Как | Статус | |---|---|---| -| Нависания (приближённо) | `list_faces`, затем `list_faces(index=N)`: для **нижних** граней угол поверхности от вертикали; > `θ_max` → флаг | ✅ плоские; ⚠️ криволинейные грубо | +| Нависания (приближённо) | `list_faces(normal="0,0,-1", normalTolerance=90−θ_max, minZ=)` — весь улов кандидаты; подробности `list_faces(index=N)` | ✅ плоские; ⚠️ криволинейные грубо | | Ориентация (геом. прокси) | `describe_model(sections="box")`: как ось слоёв соотносится с габаритом | ⚠️ длинная ось ≠ путь нагрузки | | Горизонтальные круглые отверстия | `list_faces(index=N)`: цилиндр с горизонтальной осью → «нужен teardrop» | ✅ | +| Пролёт моста | `measure(kind1="face", index1=…, kind2="face", index2=…)` между внутренними гранями опор | ✅ | +| Устойчивость высокой печати | `describe_model(sections="mass")` — центр масс; `list_edges(minZ=, maxZ=)` — очертание опорного пятна по концам рёбер (площадь пятна из `list_faces` для этого не годится) | ✅ проекция ЦМ; ⚠️ порог по высоте — эвристика | | Номиналы / зазоры / габариты | `measure` между гранями; `describe_model(sections="box")` | ✅ | | Тело / манифолд перед выдачей | `describe_model(sections="bodies")` (одно тело?), итог проверки построения в ответе операции | ✅ | +**Почему допуск отбора нависаний равен `90−θ_max`.** `θ_max` меряют от вертикали, а отбор идёт по +НОРМАЛИ: у поверхности, наклонённой на `θ` от вертикали, нормаль отстоит от −Z на `90−θ`. Значит +нависание (`θ > θ_max`) — это в точности «нормаль ближе `90−θ_max` к −Z». При дефолтном `θ_max`=45° +допуск численно равен 45, и это совпадение сбивает: для PETG с `θ_max`=40° правильный допуск **50**, +а не 40. + +**И почему у отбора нависаний обязано быть `minZ`.** Отбор по нормали ЗНАКОВЫЙ: грань проходит, +если её нормаль отклонилась от заданного направления не более чем на допуск. У грани, лежащей на +столе, внешняя нормаль равна ровно (0, 0, −1) — отклонение нулевое, значит она проходит при ЛЮБОМ +допуске и по описанному критерию («тем хуже, чем ближе к чистому −Z») выглядит худшим нависанием +детали. Замер: тот же отбор без `minZ` (допуск 45°) на кронштейне вернул девять граней, наибольшая — +515.44 мм² с центром ровно на `Zmin` (−50.50) — и это опорное пятно, а не нависание. +Нижняя граница окна (`minZ = Zmin + 0.1`, `Zmin` из `describe_model(sections="box")`) убирает +первый слой из улова. + +**Пролёт и пятно — числа, а не впечатления.** Замеры на кронштейне камеры: пролёт между стойками +**49.0 мм** (потолок PLA по §2 `fdm-rules.md` — 15–25 мм, то есть редизайн); опорное пятно +«стоя» — `list_edges(minZ=−51, maxZ=−50)` дал четыре ребра, `X −50.50…50.50`, `Y 0.00…8.00`, то +есть **101 × 8 мм**, а ЦМ из `sections="mass"` — `(0.00, 4.93, 56.61)`: проекция внутри пятна, но +высота ЦМ над столом **107.1 мм** против полуширины **4 мм** — 27× при пороге 3×. Ни то, ни другое +не выводится из эскиза арифметикой — оба стоят одного вызова. + ## Граница честности - **Угол нависания** мерить в **той же конвенции, что fdm-rules.md** (от вертикали; нижняя грань с поверхностью > `θ_max` от вертикали = нависание) — не путать с углом нормали от горизонтали. -- **Путь нагрузки агент НЕ выводит из габарита** — берёт из задачи/опроса (правило 1). Длинная ось - ≠ несущая. +- **Путь нагрузки агент НЕ выводит из габарита** — берёт из задачи, из опроса, а работая автономно + — из постановки, и тогда записывает допущением (правило 1). Длинная ось ≠ несущая. +- **«Нагрузка в XY» — требование с порогом, а не догма.** Обязательность ориентации решает запас + `σ_меж/σ` (SKILL.md, §«Ориентация: когда три требования не сходятся»); при запасе ≥10× выбор + делают нависания и устойчивость, а не слои. - **Истинная мин. толщина стенки и полный детект криволинейных нависаний — не решаются** (нет thickness/overhang-солвера). - **Аудит эвристический и НЕ доказывает печатнопригодность** (не ловит анизотропию/путь нагрузки). @@ -29,5 +56,7 @@ 1. После построения, убедившись что оно чистое, — пройти таблицу выше сверху вниз. 2. Каждый флаг — сверить с соответствующим правилом `fdm-rules.md` и решить: исправить геометрию или принять осознанно. -3. Путь нагрузки и косметические грани взять из ответа пользователя (правило 1), не из габарита. -4. Затем — чек-лист печатнопригодности (SKILL.md) → экспорт через `export_model` (`.stl` в слайсер). +3. Путь нагрузки и косметические грани взять из ответа пользователя или из постановки (правило 1), + не из габарита; ориентацию закрыть прикидкой `σ_меж/σ`. +4. Затем — чек-лист печатнопригодности (SKILL.md) → выдача; обменный файл (`export_model`) — + только если о нём просили. diff --git a/release.json b/release.json index d1ce6d4..3273d02 100644 --- a/release.json +++ b/release.json @@ -1,6 +1,6 @@ { - "version": "0.2.1", - "tag": "v0.2.1", - "sourceSha": "c66bd2efc372ce5f1bc73222fbdb19fe935ec4dd", - "built": "2026-08-07T10:20:44Z" + "version": "0.3.0", + "tag": "v0.3.0", + "sourceSha": "fcedb44f5661313ffd0570e3808a5309f6ae8cb7", + "built": "2026-08-16T10:02:12Z" }