Pull to refresh

Знакомьтесь: FreeCAD

Reading time 9 min
Views 75K
FreeCAD — параметрический трехмерный редактор, позволяющий создавать объемные модели и чертежи их проекций.
Текущая версия FreeCAD — 0.12, но можно также скачать бета-версию 0.13 и попытаться ее скомпилировать.

FreeCAD поддерживает несколько различных форматов документов как на импорт, так и на экспорт, а также позволяет сохранять «снимки экрана» (в т.ч. в формате pdf).

Документация к FreeCAD довольно обильная, но все-таки не настолько полная, насколько бы хотелось. Вопросы по использованию этого продукта можно обсудить на форуме FreeCAD.




Для удобства рисования во FreeCAD есть возможность написания скриптов на питоне, а для мелких операций можно использовать консоль питона. Консоль, правда, элементарная: истории команд не имеет.
Я не буду переписывать многотомное руководство пользователя FreeCAD, а просто приведу пример того, что можно сделать с его помощью.

Но для начала поясню, как заставить FreeCAD «видеть» свои скрипты. Перед каждым запуском FreeCAD просматривает содержимое системных директорий, а также директории ~/.FreeCAD/Mod/. Находящиеся там скрипты он и будет использовать в дальнейшем.

Скрипты во FreeCAD делятся на две категории: скрипты, вызываемые из командной строки (для их запуска необходимо сначала подгрузить командой питона import содержащий их модуль), а также скрипты, запускающиеся из GUI. Первые вы всегда можете перезагрузить командой reload, а вот для перезагрузки вторых (если вы внесли какие-то изменения в исходники), необходимо будет перезапустить FreeCAD. Сначала рассмотрим работу с первыми.

Рисуем «колесо»


Создадим эскиз (sketch) в XY-плоскости, на нем нарисуем профиль «половинки» нашего колеса.



Для этого сначала «на глаз» нарисуем основные формы в виде ломаной линии.
Далее в нужных местах срежем фаски (инструмент «fillet»). А после этого — расставим привязки точек. Начальные точки привяжем абсолютно. Параллельные объекты ограничим параллельностью, зададим расстояния между частями профиля и радиусы скруглений.



Затем завершим редактирование эскиза, выберем его в списке объектов (слева) и нажмем кнопку «вращать выбранный эскиз». Получим такое колесо:



Теперь создадим в «диске» выемки. Для этого сделаем еще один эскиз в плоскости XZ. В нем из отрезков и дуг сделаем вырез. Чтобы наше отверстие было точно симметрично относительно центра, сделаем привязку к двум невидимым линиям.



Вытянем наш эскиз на 100 единиц (чтобы он уж наверняка пересекал колесо) и переместим его (Plasement.Position) на 50 единиц по оси Y:



Теперь нам нужно «размножить» полученную фигуру по окружности, чтобы затем сделать вырезы в «колесе». Для «размножения» напишем скрипт NCopy.py:

import FreeCAD, FreeCADGui, Part
from FreeCAD import Base

def CopyObj(obj):
	name = obj.Name
	shape = obj.Shape
	newshape = shape.copy()
	newobject = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
	newobject.Shape = newshape
	return newobject

def copyCirc(C=Base.Vector(0,0,0), A=Base.Vector(0,0,1), Ang=90, N=4, rot=True, moveOriToGrp=False):
	Ang %= 360
	if (N > 360/Ang or N == 0):
		N = int(360/Ang)
	sel = FreeCADGui.Selection.getSelection()
	if (not sel):
		FreeCAD.Console.PrintError("Error: you should select some objects")
		return None
	doc = FreeCAD.activeDocument()
	grp = doc.addObject("App::DocumentObjectGroup", "CircularCopy")
	for Obj in sel:
			A0 = Ang
			for i in range (1, N):
				newobject = CopyObj(Obj)
				S = newobject.Shape
				S.rotate(C, A, A0)
				if (not rot):
					S.rotate(S.Placement.Base, A, -A0)
				grp.addObject(newobject)
				A0 += Ang
			if (moveOriToGrp):
				grp.addObject(Obj)
	return grp


Сохраним его в какой-нибудь поддиректории из ~/.FreeCAD/Mod/.

Функция CopyObj имеет вспомогательное значение (она нам пригодится в дальнейшем): эта функция просто создает копию данного объекта.

Функция copyCirc имеет шесть аргументов, для всех них назначены значения по умолчанию, поэтому вызывать его можно только указывая аргументы, имеющие другие значения. Ее аргументы:
  • C — координаты центра окружности, по которой происходит копирование;
  • A — нормаль к плоскости окружности;
  • Ang — угол между копиями;
  • N — количество копий (0 — если надо разместить объекты по всей окружности);
  • rot — вращать ли сам объект при копировании: если этот параметр равен False, объект будет распределен по окружности параллельным переносом, а не вращением;
  • moveOriToGrp — помещать ли копируемый оригинал в общую группу созданных объектов.

Радиус окружности нам не нужен — им мы считаем расстояние от объекта до центра окружности. Объект[ы] для копирования бер[е|у]тся из выделения. Если ничего не выделено, просто выдаем текст ошибки и завершаем функцию. Далее мы создаем в функции группу «CircularCopy» и начинаем перебирать все объекты из выделения. Каждый из них (N-1) раз копируется по окружности, а затем помещается в группу.

Теперь прикинем: нормаль к окружности, вдоль которой надо «размножать» объекты, имеет значение (0,1,0), угол между копиями сделаем равным 30 градусам, центр расположен по умолчанию (в начале координат), заполнить надо всю окружность. Таким образом, нам надо дать команды

import NCopy
NCopy.copyCirc(A=App.Vector(0,1,0), N=0, Ang=30, moveOriToGrp=True)


Раскрываем появившуюся группу «CircularCopy», выделяем все объекты в ней и выполняем их объединение, появится объект «Fusion». Теперь выделим наше «колесо» («Revolution») и это объединение, нажав клавишу Ctrl, и выберем операцию «пересечение». В итоге в нашем «колесе» появятся вырезы:



И вот так будет выглядеть «колесо», экспортированное в pdf:


Разное


Добавлю еще несколько функций для копирования объектов.

Копирование объекта в заданное расположение (mv — вектор сдвига, axe — ось поворота, rot — угол поворота):

def CopyObjAt(obj, mv=Base.Vector(0,0,0), axe=Base.Vector(0,0,0), rot=0):
	obj = CopyObj(obj)
	obj.Placement =  FreeCAD.Placement(mv, axe, rot)
	return obj


Копирование N раз на вектор V:

def copyVec(N=2, V=Base.Vector(0,0,1), moveOriToGrp=False):
	sel = FreeCADGui.Selection.getSelection()
	if sel:
		doc = FreeCAD.activeDocument()
		grp = doc.addObject("App::DocumentObjectGroup", "VectorCopy")
		for Obj in sel:
				R0 = V
				for i in range (1, N):
					newobject = CopyObj(Obj)
					newobject.Shape.translate(R0)
					grp.addObject(newobject)
					R0 += V
				if (moveOriToGrp):
					grp.addObject(Obj)
	else:
		FreeCAD.Console.PrintError("Error: you should select some objects")
	return grp


Копирование N раз по траектории — B-сплайну:

def copyByTrajectory(N=4):
	sel = FreeCADGui.Selection.getSelection()
	def prErr():
		FreeCAD.Console.PrintError("Error: you should select a traectory and one object\n")
	if (not sel):
		prErr(); return None
	L = len(sel)
	if(L != 2):
		prErr(); return None
	if(N < 2):
		FreeCAD.Console.PrintError("Error: N shold be more than 1\n"); return None
	Traj = sel[0].Shape
	#(a, b) = Traj.ParameterRange
	Obj = sel[1]
	TLen = Traj.Length / (N - 1)
	curPos = 0
	doc = FreeCAD.activeDocument()
	grp = doc.addObject("App::DocumentObjectGroup", "BSplineCopy")
	try:
		for i in range(0, N):
			v = Traj.valueAt(curPos); curPos += TLen
			newobj = CopyObjAt(Obj, v)
			grp.addObject(newobj)
	except:
		FreeCAD.Console.PrintError("Error: bad selection\n")
		return None


Рисуем подшипник


Теперь аналогично «колесу» мы можем нарисовать простой подшипник без сепаратора.

Для начала нарисуем обечайки. Опять-таки, «на глаз» нарисуем сначала контуры из ломаных линий, затем между ними нарисуем окружность, которую сразу же сделаем неактивной. Далее — наложим на наши фигуры ограничения: положения точек, вертикальность и горизонтальность участков, равенство одинаковых линий, тангенциальное ограничение одной точки борозды в обечайках и поверхности шарика, внесем кое-какие размеры. В итоге получим такой эскиз:



Выйдем из режима редактирования эскиза, выделим его на общем виде и активируем операцию вращения эскиза. В результате у нас получится вот такая симпатичная штука:



Теперь нам надо добавить шарики. Для этого активируем значок «Нарисовать сферу», чтобы получить сферу по умолчанию. Выделим ее и исправим параметры: радиус сделаем равным 15 единицам, а в положении (Placement.Position) координату X сделаем равной 58 единиц. Для того, чтобы «размножить» шарики, нам еще надо бы вычислить угловой диаметр одного шарика. Проще всего это сделать, опять открыв наш эскиз (теперь он принадлежит объекту «Revolution»). Чтобы обечайка не мешалась, выделим ее и нажмем пробел — она станет невидимой. Теперь нарисуем отрезок из начала координат к нашему шарику. Сделаем ограничения: один конец отрезка строго привяжем к началу координат, а второй сделаем касательной к шарику. Выделив этот отрезок, активируем ограничение по углу (конечно, FreeCAD будет ругаться на чрезмерное количество ограничений, но нам надо только посмотреть). Итак, этот угол равен 14.9882 градуса, грубо говоря — 15 градусов. Сразу же удалим это ограничение, чтобы FreeCAD не ругался и вернемся к нашим баранам общему виду. Выделим шарик и распределим его копии по окружности через каждые 30 градусов. По счастливой случайности нам нужно будет ввести аналогичные предыдущему пункту команды:

import NCopy
NCopy.copyCirc(A=App.Vector(0,1,0), N=0, Ang=30, moveOriToGrp=True)


Теперь немного раскрасим наши объекты. Чтобы было проще раскрашивать шарики, объединим их в один объект. Для «красоты» сменим стиль отображения объектов с «Flat Lines» на «Shaded». Цвет шариков я сделал «золотым» (255, 214, 0), а цвет обечайки — «серебряным» (192, 192, 192). Можно цвет изменять не через GUI, а в командной строке (только RGB компоненты надо нормализовать на единицу). Если документ называется «bearing», то это будет выглядеть так:

FreeCADGui.getDocument("bearing").getObject("Fusion").ShapeColor = (1.00,0.84,0.00)
FreeCADGui.getDocument("bearing").getObject("Revolution").ShapeColor = (0.75,0.75,0.75)


Точно так же можно и изменить тип их отображения:

FreeCADGui.getDocument("bearing").getObject("Revolution").DisplayMode = "Shaded"
FreeCADGui.getDocument("bearing").getObject("Fusion").DisplayMode = "Shaded"




И можно сделать чертеж и экспортировать его в SVG:



Еще о скриптописании


Как я уже говорил, для того, чтобы сделать возможным запуск своих команд из консоли FreeCAD, достаточно лишь файлы (модули питона) с этими командами разместить в директории ~/.FreeCAD/Mod/ или ее поддиректориях. Однако, чтобы сделать возможным запуск команд из GUI («кнопочки», «меню» и т.п.), необходимо сделать немного больше.

Во-первых, необходимо создать инициализирующий скрипт InitGui.py и поместить его в скриптовую поддиректорию. Этот скрипт FreeCAD запустит при просмотре содержимого нашей скриптовой директории.

Минимальное содержимое этого файла таково:

class MyWorkbench ( Workbench ):
	 import список файлов
	 Icon = путь к миниатюре инструментария (модуля)
	 MenuText = "Название модуля"
	 ToolTip = "Пояснения во всплывающей подсказке"
	 def Initialize(self):
		# ToolBar
		list = [Список предоставляемых пунктов для панели инструментов]
		self.appendToolbar("Название панели инструментов",list)
		# Menu
		list = [Список предоставляемых пунктов для панели меню]
		self.appendMenu("Название меню",list)
Gui.addWorkbench(MyWorkbench())


«Списки пунктов» list содержат строковые переменные — названия предоставляемых нашим инструментарием классов. Соответственно, для каждой строки из этих списков мы должны создать соответствующую команду или класс. При активации миниатюры на панели задач или пункта меню будет запущен конструктор этого класса, выполняющий какое-то действие. Минимальное содержимое этого класса таково:

class OneAction:

	def Activated(self): #Initialize
		import список файлов
		функции, выполняемые при активации пункта меню или миниатюры

	def GetResources(self):
		import список файлов
		IconPath = путь к миниатюре действия (для панели задач)
		MenuText = "Текст пункта в меню"
		ToolTip = "Подсказка"
		return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}

FreeCADGui.addCommand('Название пункта', OneAction())


Последняя строка говорит FreeCAD'у, что нашему классу OneAction будет соответствовать строка «Название пункта» (соответственно, эту строку мы поместим в списки класса-инициализатора нашего инструментария).

Для того, чтобы создавать новые объекты, мы должны для этих объектов создать отдельный класс. Минимальное содержимое этого класса таково:

class MyObject:
	def __init__(self, obj, значения):
		''' Add new properties to partfeature '''
		obj.addProperty("Тип свойства","Название свойства","Объект","Комментарий").свойство=значение
		obj.Proxy = self
		obj.Shape = функция построения объекта
		Класс-провайдер(obj.ViewObject)

	def onChanged(self, obj, prop):
		действия, выполняемые при изменении свойства prop, obj - наш объект
		Класс-провайдер(obj.ViewObject)

	def execute(self, obj):
		действия, выполняемые при "выполнении" объекта
		Класс-провайдер(obj.ViewObject)


Класс-провайдер обеспечивает GUI ресурсами (значками и т.п.).

Примеры скриптов можно посмотреть в директориях ресурсов FreeCAD и в официальной документации.

Напоследок приведу примеры. Первый пример нарисован без использования командной консоли:



Второй пример — при помощи несложных скриптов, позволяющих рисовать линзы. Вот где, как говорится, «лучше день потерять, зато потом за час долететь». Вот такой код:

import Lens
a=[[8,6,1,6,3], [10,-15,2.5,8,5], [-16,10,0.5,6,7], [-8,-6,0.5,4,3], [3,3,0.2,2,1], [0,0,0.1,1,0.1], [0,3,.3,1]]
Lens.makeLensBench(a)
b=[[-10,8,0.1,6], [8,16,0.5,6], [16,-9,1,6]]
Lens.makeLensBench(b,App.Vector(0,10,0))


позволяет получить такое:



На видео можно посмотреть, как выглядит эта модель.

Заинтересовавшимся советую зайти еще сюда и посмотреть еще множество всяких разных примеров.

UPD: вот пример реальной работы, выполненной во фрикаде (криостат).
Tags:
Hubs:
+59
Comments 89
Comments Comments 89

Articles