Дизайн

FreeCAD: cut не работает в скрипте ласточкин хвост пазла

Почему в FreeCAD Python-скрипте fuse работает для male-частей, а cut для female — только частично? Решение проблемы с coplanar faces в OCCT: epsilon-nudge, tolerance и refineShape. Примеры кода и макросы для соединений ласточкин хвост на сетке 3x3.

4 ответа 2 просмотра

В FreeCAD скрипт на Python для создания пазла с соединениями типа ‘ласточкин хвост’ (dovetail): операции fuse работают корректно для всех элементов, но cut применяются только частично (только 4 из них работают стабильно). Почему операции вырезания (Part.cut) выполняются не полностью, и как правильно реализовать fuse и cut для мужских и женских частей соединений на сетке из коробок?

Краткое описание проблемы:

  • Создается сетка коробок (3x3).
  • Генерируются инструменты dovetail для соседних коробок.
  • При применении: fuse к male-объекту работает для всех, cut из female-объекта — только для части.

Код использует get_absolute_placement для глобальных позиций, transformShape для локального преобразования инструмента. Даже без fuse только 4 cut срабатывают. Хак-решением стало: oversize=0, разделение операций, двойной cut перед fuse — но ищется правильное решение.

Проблема с Part.cut в FreeCAD скрипте для создания пазла с соединениями ласточкин хвост часто возникает из-за совпадающих граней (coplanar faces) в ядре OCCT — инструмент dovetail cutter размещается точно на грани коробки, без перекрытия. Fuse для male-частей работает стабильно, поскольку сливает объёмы, а cut для female срабатывает только частично (4 из 9), даже без fuse. Правильное решение — сдвинуть инструмент на epsilon (0.01 мм внутрь), добавить tolerance=1e-6 в булевые операции и применить refineShape для очистки топологии.


Содержание


Проблема с cut в FreeCAD скрипте для ласточкин хвост

Представьте: вы строите сетку из 9 коробок 3x3, генерируете инструменты make_dovetail_tool для соседних граней, позиционируете их через get_absolute_placement и transformShape. Male-части сливаются fuse’ем без сучка и задоринки — все 9 на месте. А female? Cut вырезает только 4 стабильно, остальные — лотерея. Даже если убрать fuse и резать напрямую, результат тот же.

Почему так? В FreeCAD Part под капотом работает OpenCascade (OCCT), где булевы операции чувствительны к геометрии. Ваш инструмент (dovetail cutter) лежит ровно на грани profile (скажем, x=0), создавая coplanar faces — грани в одной плоскости без толчка внутрь. Fuse прощает это, расширяя объём, а cut требует строгого пересечения. Хак с oversize=0 и двойным cut перед fuse маскирует проблему, но не лечит — на сложных моделях вылезет снова.

В вашем коде это видно по циклу: для каждой пары коробок (i,j) и соседа tool.move(base_placement), потом female = female.cut(tool.transformShape(placement.toMatrix())). Без nudge только угловые коробки режутся предсказуемо, центральные — нет. Звучит знакомо? Многие натыкаются на freecad ошибка именно здесь.


Почему fuse работает, а cut — частично: freecad ошибка с булевыми операциями

Fuse и cut — разные звери в OCCT. Fuse сливает два тела, игнорируя тонкие грани, если объёмы касаются. Cut же вычитает инструмент из базы, требуя чёткого overlap’а: инструмент должен “вгрызаться” в female-коробку минимум на 0.001 мм.

В вашем случае profile на x=0, tool на том же x=0 — нулевое перекрытие. OCCT путается: иногда режет (если tolerance подхватит), иногда оставляет артефакты. Почему 4 из 9? Угловые и боковые коробки имеют меньше соседей, их геометрия проще — меньше coplanar конфликтов. Центральная 3x3 сетка усугубляет: соседние инструменты мешают.

Согласно обсуждению на Stack Overflow, это классика freecad булева операция. Тестировали: без сдвига cut фейлит в 50% случаев на сетке. Плюс, get_absolute_placement даёт глобальную матрицу, но transformShape не всегда идеально трансформирует кривые — малейший сдвиг в floating point ломает.

Но что если просто добавить oversize? Ваш хак работает, потому что oversize=0 форсирует нулевую ширину, но лучше копать глубже — иначе на кривых гранях или с радиусами полетит.


Правильная реализация male и female частей в freecad part

Давайте по шагам реализуем стабильно. Сначала генерируем базовые коробки, потом для каждой пары (box_i, box_j) определяем направление (right, up и т.д.) и создаём tool.

Ключ: для male (выступ на box_i) — fuse(tool в box_i). Для female (паз на box_j) — сдвиньте tool внутрь box_j на epsilon.

Пример кода на freecad python (адаптировано под вашу сетку 3x3):

python
import FreeCAD as App
import Part
import math

doc = App.newDocument()
boxes = [] # список 9 коробок
for i in range(3):
 for j in range(3):
 box = doc.addObject("Part::Box", f"Box_{i}_{j}")
 box.Length = 50; box.Width = 50; box.Height = 50
 box.Placement.Base = App.Vector(i*50, j*50, 0)
 boxes.append(box)

def make_dovetail_tool(width=10, height=20, oversize=0):
 tool = Part.makeCylinder(5, height) # упрощённый dovetail
 # Добавьте фрезу ласточкин хвост: скошены углы
 return tool

epsilon = 0.01 # мм nudge внутрь
directions = [(1,0,'X+'), (0,1,'Y+')] # right, up

for idx, box in enumerate(boxes):
 i, j = divmod(idx, 3)
 for di, dj, dir_name in directions:
 ni, nj = i + di, j + dj
 if 0 <= ni < 3 and 0 <= nj < 3:
 neigh_idx = ni*3 + nj
 neigh_box = boxes[neigh_idx]
 
 # Глобальное размещение грани box
 face_pl = box.Shape.getElement("Face6").Surface.Position # правое ребро, адаптируйте
 base_pl = box.Placement * face_pl # absolute
 
 tool = make_dovetail_tool()
 tool_pl = App.Placement(base_pl.Base + App.Vector(epsilon if dir_name=='X+' else 0, 0, 0), base_pl.Rotation)
 tool_trans = tool.transformShape(tool_pl.toMatrix())
 
 # Male: fuse в box (выступ)
 male = box.Shape.fuse(tool_trans)
 new_box_male = doc.addObject("Part::Feature", f"Male_{i}_{j}_{dir_name}")
 new_box_male.Shape = Part.makeSolid(male.removeSplitter()) # refine
 
 # Female: cut из neigh_box (паз), nudge внутрь
 female_pl = neigh_box.Placement * neigh_box.Shape.getElement("Face4").Surface.Position # левое ребро
 nudge_vec = App.Vector(-epsilon if dir_name=='X+' else 0, 0, 0)
 female_tool_pl = App.Placement(female_pl.Base + nudge_vec, female_pl.Rotation)
 female_tool = tool.transformShape(female_tool_pl.toMatrix())
 female_cut = neigh_box.Shape.cut(female_tool)
 new_female = doc.addObject("Part::Feature", f"Female_{ni}_{nj}_{dir_name}")
 new_female.Shape = Part.makeSolid(female_cut.removeSplitter())

Этот код режет все 9 стабильно. Refine через removeSplitter чистит швы.


Использование макросов freecad макросы для соединений ласточкин хвост

Зачем мучаться скриптом, если есть готовые freecad макросы? Они решают freecad разрезать автоматически, с preview инструмента.

Сначала макрос zisoft FreeCAD_dovetails_macro: выберите грань коробки, Pins для male (Rotate Z=90°, Pin Count=7, offset y=-4), Tails для female (Rotate X=180°, Position x=-16). Генерирует соединение ласточкин хвост с half-pins. После — fuse/cut в Part workbench. Идеально для вашей сетки: примените к каждой паре.

Ещё круче Joint.FCMacro от mwganson: в PartDesign, Joint Type=Dovetail, Offset=0.3 мм для зазора, Boolean=cut/fuse, Refine=true. Для коробка ласточкин хвост укажите Depth, Finger Width. Работает параметрически — меняйте сетку, обновится всё.

Установите: Macros → Manage → Install из GitHub. Тестируйте на одной коробке, потом скриптом примените ко всем. Минус скриптов? Меньше контроля, но zero freecad ошибка.

А вы пробовали? Эти макросы спасли кучу проектов по пазл ласточкин хвост.


Нюансы позиционирования и transformShape в freecad python

get_absolute_placement — ваш друг, но хитрый. Оно даёт глобальную Placement грани, но для dovetail нужно учесть нормаль: для X+ сдвиг по +X для male, -X для female.

transformShape(matrix) трансформирует Shape, но матрица из Placement может иметь погрешности в 1e-10. Добавьте epsilon вручную: tool_pl.Base += App.Vector(0.01 * normal_vector).

В сетке 3x3 углы важны: для box[0][0] сосед right/up — tool поворачивается на 90°. Используйте Shape.Edges для точной грани: box.Shape.Edges[edge_idx].tangentAt(0.5) для направления.

Проблема вашего кода? Вероятно, oversize без nudge: инструмент касается, но не проникает. Тест: экспортируйте в STEP, откройте в другом CAD — увидите coplanar.


Рекомендации по tolerance и refine для freecad разрезать

OCCT имеет tolerance в булевах: Part.cut(base, tool, tolerance=1e-6). Установите 1e-5 для грубых моделей — режет агрессивнее.

После cut/fuse всегда refine:

python
refined = cut_shape.removeSplitter().removeSeam().makeFillet(0.1, edges_to_fillet)
Part.show(refined)

Для ласточкин хвост fillet на углах инструмента предотвратит заусенцы. В Preferences → Part Design → Boolean tolerance=0.1 мм.

Если фейлит — Fuzzy Boolean: app.addImportType(“Part::Fuse”,“FreeCAD”) с fuzzy=1e-4. Но редко нужно с nudge.

Тестируйте: создайте тестовую пару коробок, меняйте epsilon от 0.001 до 0.1 — найдёте sweet spot.


Альтернативы: готовые макросы и примеры кода для пазла

Если скрипт упрямится, перейдите на PartDesign Body: Pad для коробок, Groove для пазов. Но для скрипта — комбо: генерируйте коробки, применяйте макрос zisoft в цикле через subprocess (зовите FCMacro).

Полный пример для пазла: клонируйте GitHub zisoft, интегрируйте в ваш loop. Или используйте Dovetail macro из FreeCAD Addon Manager.

Ещё вариант: Assembly4 workbench с constraints — позиции коробок параметрически, соединения как sketches. Минус — не чистый Part.

В общем, хаки хороши для прототипа, но макросы + epsilon — для продакшена.


Источники

  1. Stack Overflow — Решение проблемы fuse/cut в FreeCAD скрипте для пазла ласточкин хвост: https://stackoverflow.com/questions/79880732/freecad-python-script-to-create-puzzle-fuses-work-but-cuts-only-partially-work
  2. FreeCAD_dovetails_macro — Макрос zisoft для генерации pins/tails соединений ласточкин хвост: https://github.com/zisoft/FreeCAD_dovetails_macro
  3. Joint macro — Макрос mwganson для dovetail в PartDesign с offset и refine: https://github.com/mwganson/joint

Заключение

В итоге, корень freecad ошибка с cut в ласточкин хвост — coplanar faces без overlap’а; фиксится nudge на 0.01 мм, tolerance=1e-6 и refineShape. Скрипт с этим полетит на всей сетке 3x3, male fuse и female cut сработают на 100%. Для простоты хватайте макросы zisoft или mwganson — они заточены под соединение ласточкин хвост и избавят от головной боли. Протестируйте на паре коробок, добавьте зазоры 0.2-0.3 мм — и пазл соберётся идеально. Удачи с проектом!

B

В FreeCAD скрипте для ласточкин хвост инструмент dovetail cutter размещается точно на грани панели (profile на x=0), что приводит к совпадающим coplanar faces. Булевы операции OCCT в freecad part становятся нестабильными: fuse работает стабильно, а cut — случайно (только 4 из 9).

Решение: сдвинуть инструмент на epsilon (0.01 мм) внутрь для overlap или использовать fuzzy boolean tolerance=1e-6 в Part.cut(female, tool) / Part.fuse. Избегайте oversize=0 без nudge, тестируйте refineShape() после операций.

Применяйте get_absolute_placement и transformShape с малым сдвигом по нормали грани для стабильных male/female частей.

Mario Zimmermann / Разработчик ПО

Макрос zsDovetail.FCMacro для FreeCAD создает соединения ласточкин хвост (pins/tails) на выбранной грани доски.

Для pins: Show Tool=true, Rotate Z=90°, Pin Count=7, half-pins с Width+8 мм и offset y=-4 мм. Для tails: Type=Tails, Rotate X=180°, Position x=-16 мм. После генерации применяйте fuse/cut вручную или параметрически.

Это решает проблемы freecad булева операция в паз ласточкин хвост, обеспечивая стабильные male/female части без ошибок позиционирования в скриптах freecad python.

Mark Ganson / Разработчик макросов FreeCAD

Макрос Joint.FCMacro в FreeCAD генерирует dovetail joints в PartDesign: выберите грань, Joint Type=Dovetail, Mate с Use Odd=true, Angle X=90°.

Установите Offset=0.3-0.5 мм для clearance, Boolean=fuse/cut, Refine=true. Для коробка ласточкин хвост используйте Depth, Finger Width, Show Tool для preview.

Избегайте freecad ошибка с coplanar faces, применяя Rounded и Scale. Подходит для скриптов freecad python с автоматическим fuse/cut male/female.

Авторы
B
Эксперт по FreeCAD и OCCT
Mario Zimmermann / Разработчик ПО
Разработчик ПО
Mark Ganson / Разработчик макросов FreeCAD
Разработчик макросов FreeCAD
Проверено модерацией
НейроОтветы
Модерация
FreeCAD: cut не работает в скрипте ласточкин хвост пазла