22 KiB
Надписи и рельеф — точная подгонка и отделка
Дополнение к разделу «Надписи, логотипы и рельеф» 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").
Порядок для фаски по контуру логотипа — только такой, и он неочевиден:
- чистка порогом ≈ будущего катета (
minSegment); - замер ЧИЩЕНОГО силуэта —
sketch_measure_thickness(entitiesFile, minSegment=тот же порог); - катет по узкому месту чищеного силуэта:
c ≤ (узкое_место − 2·w)/2, гдеw— ширина нити.
Менять шаги местами нельзя: чистка СУЖАЕТ узкое место. Она заменяет скругление острым углом — в выпуклом углу это добавляет материал (габарит подрастает), а в вогнутом срезает его, и именно вогнутые углы образуют перемычки между штрихами. Сужение доходит до единиц процентов узкого места — этого хватает, чтобы катет, подобранный по нечищеному контуру, перестал проходить свой же критерий. Числа снимай на СВОЁМ контуре: они принадлежат рисунку, а не приёму.
- Ширину штриха и узкое место меряй инструментом, а не глазом —
sketch_measure_thicknessничего не строит и документа не требует, поэтому зовётся ДО эскиза (help(topic="silhouette-thickness")). От этих чисел зависит, останется ли что-то от рисунка после фаски и напечатается ли он вообще; шаг растра в ответе — точность округления катета.
Подгонка ширины и кегля
widthFactorменяет пропорцию крупно,height— точно. Замер (Lobster «OldMan», h=17): wf 1.00 → 71.84 мм, wf 1.25 → 92.83 (+29 %) — то есть это настоящий масштаб ширины, и без него скриптовый шрифт в вытянутое поле не вписать. Но КОМПАС квантует его ступенями: wf 1.25, 1.26, 1.27, 1.28 дают ячейку 100.64 и ширину 92.83…93.14 (+0.3 %), а на 1.29 ячейка прыгает на 103.34 и ширина сразу 95.49 (+2.5 %). Отсюда правило: пропорцию задаёшьwidthFactor, точную ширину добираешьheight— подгонка через wf топчется на ступени.heightдвигает ширину плавно, а высоту — ступенями. Замер (Lobster «OldMan», wf 1.18): h 17.00 → глифы 85.37 × 17.07; h 16.94 → 85.11 × 17.07 (ширина отозвалась, высота нет); h 16.50 → 82.90 × 16.54; h 15.50 → 77.95 × 15.60. Значит подгонять ширину кеглем можно, но вертикаль по нему не считается: после пересчёта перечитай фактический габарит из ответа и привязывай верх/низ блока ТОЧКОЙ ВСТАВКИ, а не кеглем.- Кегль под заданный габарит считается за ОДНУ пробу, а не подбирается. При фиксированном
widthFactorприрост каймы Δ (сумма по обеим сторонам) от кегля не зависит — фигура подобна. Значит: построил пробу с любымheight₀, замерил ширину глифовW₀и суммарный прирост Δ, дальшеheight = height₀ · (W_цель − Δ) / W₀— и целевой габарит получается с первого раза. Δ бери только из СВОЕЙ пробы — на том же шрифте,widthFactorиthinThickness. Числа из этого файла (Lobster: Δ=4.62 при t=1.5/wf 1.29; Δ=4.93 при t=1.6/wf 1.18; Δ=2.92 при t=0.9/wf 1.33) приведены как порядок величины, а не как константа: подставив чужое Δ, промахнёшься на проценты по ширине — а именно её ты и подгоняешь. - Шаг второй, без которого формула обманывает: проверь ВЫСОТУ, которая из неё вышла. Кегль
подобран по ширине, но вместе с ним задалась и высота блока букв — а от неё зависит, дотянется ли
кайма до плашки. Считай до построения:
низ каймы = низ глифов − t, и он обязан зайти на плашку. Замер (Lobster «OldMan», целевая ширина 90, кайма t=1.5): приwf 1.29формула даёт ровно 90.00 по X и глифы высотой 15.2 → низ каймы 11.77 против верха плашки 10.5, разрыв 1.27 мм, деталь распалась бы на два тела; приwf 1.18— глифы 17.07 → низ каймы 9.93, перекрытие 0.57 мм, одно тело. Отсюда рабочее правило:widthFactor— ручка не пропорции, а стыковки. При фиксированной целевой ширине большийwfдаёт более низкий блок букв, и вертикаль проигрывается: сначала подбериwfтак, чтобы высота блока перекрыла плашку, и только потом добирай точную ширину кеглем.
Кайма (тонкая стенка вокруг букв)
- Кайма на острых терминалах шире
thinThickness— и по каждой стороне по-своему. Стенкаoutwardобходит контур с миттером: на остром окончании глифа габарит растёт на сторону наt / sin(θ/2), а не наt(Lobster: слева +0.90 при t=0.9, справа +1.70 — терминал «n»). Величина принадлежит не шрифту, а КОНКРЕТНОМУ начертанию и толщине стенки, поэтому у каждого числа держи в уме пару (t, wf): Lobster «OldMan» при t=0.9 дал слева +0.90 / справа +1.70 при wf 1.18 и +0.90 / +2.09 при wf 1.40; при t=1.5 — +1.50 / +3.12. Общее в них одно: сбоку прирост равен t, а на остром терминале — t/sin(θ/2), и переносить абсолютные числа между вариантами нельзя — перезамеряй после сменыwidthFactorилиthinThickness. - По вертикали кайма прибавляет ровно 2·t. Сверху и снизу глифов миттера нет (касательные ровные), поэтому высота блока = высота глифов + 2·t считается точно, без пробы; замером это подтверждается на всех вариантах (t=0.9 → +1.80). Мерить нужно только ширину.
align/vAlignцентруют ГЛИФЫ, а кайма несимметрична — значит центр надо считать. Просьба «поставить надпись по центру поля 90 мм» черезalign="center", x=45даёт силуэт, съехавший на полразницы приростов: у Lobster при t=1.5 слева +1.50, справа +3.12. Целься не в центр поля, а в тот X, при котором СИЛУЭТ ляжет в поле:x = (Δ_лев + (L − Δ_прав)) / 2. Замер: (1.50 + 90 − 3.12)/2 = 44.19 → глифы 1.50…86.88, силуэт с каймой ровно 0…90.00. То же и по вертикали, только там прирост симметричен (t сверху и снизу), поэтомуvAlign="top"вверх поля − tпопадает без поправки.- Толщина каймы — ещё и параметр стыковки блоков. Кайма опускает силуэт надписи ниже её
глифов на
t, и именно это перекрытие с плашкой держит деталь одним телом. Считай так: низ каймы = низ глифов − t, и он должен зайти на плашку хотя бы на несколько десятых. Проверка — сводка «Тел:» в ответе самогоprimitive(union): у примитивов она надёжна, потому что сервер сам сверяет объём до и после и отвергает объединение, ничего не давшее. Уloft/sweepэтой сводке верить нельзя — там она однажды напечатала «Тел: 1», когда тел было два (см. основной навык, п. «Ответ мутирующей операции»); в сомнительном случае считай телаdescribe_model(sections="bodies"). - Кайма вокруг ПРЯМОУГОЛЬНОЙ плашки — вторым примитивом, а не тонкой стенкой. Стенка обходит
контуры эскиза, а плашка по playbook'у строится
primitive, и каймы у неё не появится. Клади под неё второй блок на2·tбольше и на высоту подложки (85 × 13.5 × 2 под плашкой 82 × 10.5 × 3) — получается тот же кант. Сводить прямоугольник в один эскиз с буквами не надо: контуры пересекутся, и эскиз станет невалидным.
Разрядка (трекинг)
- Разрядки (трекинга) у примитива нет — её набирают пробелами. Ширину пробела для конкретного шрифта и кегля вычисляют двумя пробами: «EDITION» → 38.38 мм, «E D I T I O N» → 47.11 мм, значит пробел 1.455 мм (Bevan, cap 5.5).
Отделка рельефа и соседняя геометрия
- Пакетом по контуру рельефа не строится СКРУГЛЕНИЕ — ни сверху, ни на малом радиусе; фаска
строится по обоим контурам. Скругление: на Zilla Slab «AB» (203 ребра) одиночное верхнее ребро
берёт R0.1 и R0.2, а
fillet_edgeсразу по всем отказывает при обоих радиусах — соседние скругления конфликтуют; уменьшение радиуса не спасает (Lobster, 135 верхних рёбер: отказ и при R0.3, и при R0.15). Фаска:chamfer_edge(edgeIndices=[…])проходит с первой попытки и по низу (0.3 на 59 и на 120 рёбрах), и по ВЕРХУ рельефа (0.2 на 135 рёбрах букв, отдельно 0.2 на 133 рёбрах канта плашки с надписью) — прямые, NURBS, дуги и огрызки 0.03 мм вперемешку. Значит притупление кромок по всему силуэту, хоть снизу против «слоновьей ноги», хоть сверху по требованию чертежа, — это ОДНА операция, и делается она фаской. Рёбра берутся окном:list_edges(minZ=-0.01, maxZ=0.01)— низ,list_edges(minZ=h-0.01, maxZ=h+0.01)— верх (сузить до нужной части силуэта помогаетminY/maxY). Порядок «сначала скругления углов, потом фаска» работает — дуги от R0.5 фаске не мешают. В отборе встречается типother(у сложных кривых) — он тоже принимается параметромtype. - Отказ фаски на НЕЧИЩЕНОМ контуре — не повод уменьшать катет: успех немонотонен по размеру.
На контуре с острыми клиньями между элементами
chamfer_edgeотказывает на нескольких катетах подряд и проходит на большем — проверено подряд, сrebuildмежду попытками и на неизменных индексах рёбер. Похоже, малый катет оставляет в остром клине вырожденный «нос», который КОМПАС сшить не может, а большой срезает остриё целиком. Но перебирать катет не надо: правильное лечение —minSegment(см. SKILL.md §«Надписи, логотипы и рельеф»). После чистки совет «попробуй увеличить» становится вреден: рабочий диапазон катета сдвигается ВНИЗ вместе с узким местом, и катет, проходивший на нечищеном контуре, чищеный отвергает. Не переносите подобранное значение между состояниями контура — оно к ним привязано. Прежняя формулировка добавляла «исключение самых коротких рёбер картину не меняет, то есть дело не в них» — вывод неверен: исключённое изedgeIndicesребро остаётся в теле, и это не то же самое, что убрать сегмент из контура. Убранный из контура — меняет. primitive(subtract)режет ВСЁ, что попало в его габарит — включая уже построенные буквы. Сверять надо глифы, а не кайму, и только те, что дотягиваются до кармана ПО ВЫСОТЕ: карман обычно вычитается от z основания вверх (например от 2 до 3), а кайма ростом всего 2 мм в этот слой не попадает — она может сколько угодно перекрывать плашку в плане, и именно это перекрытие держит деталь одним телом. Опасны буквы: они идут на всю высоту 3 мм, поэтому их нижняя кромка по Y должна быть выше верхней кромки кармана. Проверка постфактум — точное равенство убыли объёма расчётному (79 × 7.5 × 1.0 = 592.5 против фактических 592.5 мм³): объём приходит в сводке ответа самой операции, любое касание соседней геометрии сразу ломает равенство, а габарит и снимок его не покажут.- Углы вокруг надписи — одним
fillet_edge(points=[…]). На теле с сотнями рёбер список изlist_edgesнеподъёмен, но точки углов известны из построения: передай их списком, и все рёбра уйдут в ОДНУ операцию дерева. Если ребра в части точек нет (типичный случай — угол «съеден» каймой букв), ошибка перечислит ВСЕ такие точки сразу: убери их и повтори одним вызовом.