Представленный FreeCAD макрос создаёт тестовую модель для проверки прилипания пластика к столу при FDM-печати.
Latex TikZ код рисунка
\begin{tikzpicture}\begin{scope}[scale=0.6]
% PARAMETERS
\def\SECTIONHEIGHT{0.1}
\def\SECTIONWIDTH{0.5}
\def\VISUALRATIO{1} % визуальный коэффициент
\def\HEIGHTTOTAL{60}
\def\WIDTHTOTAL{60}
\def\LINESNUMBER{11}
% derived parameter
\pgfmathsetmacro{\xstep}{\WIDTHTOTAL/(\LINESNUMBER-1)}
% Команда для рисования сегмента как прямоугольника с контуром
\newcommand{\filamentrect}[4]{%
% #1 x1, #2 y1, #3 x2, #4 y2
\pgfmathsetmacro{\dx}{#3-#1}
\pgfmathsetmacro{\dy}{#4-#2}
\pgfmathsetmacro{\norm}{sqrt(\dx*\dx+\dy*\dy)}
\pgfmathsetmacro{\ox}{\SECTIONWIDTH*\VISUALRATIO/2 * \dy/\norm}
\pgfmathsetmacro{\oy}{-\SECTIONWIDTH*\VISUALRATIO/2 * \dx/\norm}
% Прямоугольник с заливкой и контуром
\filldraw[fill=orange!30, draw=orange!80!black, line width=0.5mm]
(#1+\ox,#2+\oy) -- (#1-\ox,#2-\oy) -- (#3-\ox,#4-\oy) -- (#3+\ox,#4+\oy) -- cycle;
}
% path generation
\pgfmathsetmacro{\x}{0}
\pgfmathsetmacro{\y}{0}
\foreach \i in {1,...,\numexpr\LINESNUMBER-1\relax} {
% determine new y
\pgfmathparse{\y<0.001 ? \HEIGHTTOTAL : 0}
\let\ynew\pgfmathresult
% determine new x
\pgfmathsetmacro{\xnew}{\x+\xstep}
% рисуем сегменты как прямоугольники
\filamentrect{\x}{\y}{\x}{\ynew}
\filamentrect{\x}{\ynew}{\xnew}{\ynew}
% update variables
\xdef\x{\xnew}
\xdef\y{\ynew}
}
% final vertical segment
\pgfmathparse{\y<0.001 ? \HEIGHTTOTAL : 0}
\let\ynew\pgfmathresult
\filamentrect{\x}{\y}{\x}{\ynew}
% AXES
\draw[ultra thick,red,-Stealth,line width=12pt] (0,0) -- (20,0) node[above left,scale=5.8]{X};
\draw[ultra thick,green!70!black,-Stealth,line width=12pt] (0,0) -- (0,20) node[left,scale=5.8]{Y};
\draw[fill=blue!60, draw=blue!60] (0,0) circle (0.8cm) node[left=1.1cm,blue,scale=5.8]{Z};
\draw[fill=blue!75, draw=blue!75] (0,0) circle (0.6cm);
\draw[fill=blue!100, draw=blue!100] (0,0) circle (0.25cm);
\end{scope}
\begin{scope}[xshift=40cm]
% PARAMETERS
\def\SECTIONHEIGHT{0.1}
\def\SECTIONWIDTH{0.5}
\def\VISUALRATIO{1}
\def\HEIGHTTOTAL{60}
\def\WIDTHTOTAL{60}
\def\LINESNUMBER{10}
% projection parameters
\def\ISOX{0.75}
\def\ISOY{0.55}
\def\ISOZ{1.1}
% derived parameter
\pgfmathsetmacro{\xstep}{\WIDTHTOTAL/(\LINESNUMBER-1)}
\pgfmathsetmacro{\halfwidth}{\SECTIONWIDTH*\VISUALRATIO/2} %%%
\pgfmathsetmacro{\scaledheight}{\SECTIONHEIGHT*\VISUALRATIO}
% projection
\newcommand{\iso}[3]{({#1 + \ISOX*#2},{\ISOY*#2 + \ISOZ*#3})}
% filament segment
\newcommand{\filamentsegment}[4]{%
% #1 x1
% #2 y1
% #3 x2
% #4 y2
% offset for width
\pgfmathsetmacro{\dx}{#4-#2}
\pgfmathsetmacro{\dy}{-(#3-#1)}
\pgfmathsetmacro{\norm}{sqrt(\dx*\dx+\dy*\dy)}
\pgfmathsetmacro{\ox}{\halfwidth*\dx/\norm}
\pgfmathsetmacro{\oy}{\halfwidth*\dy/\norm}
% top
\fill[orange!60]
\iso{#1+\ox}{#2+\oy}{\scaledheight} --
\iso{#1-\ox}{#2-\oy}{\scaledheight} --
\iso{#3-\ox}{#4-\oy}{\scaledheight} --
\iso{#3+\ox}{#4+\oy}{\scaledheight} -- cycle;
% right side
\fill[orange!50]
\iso{#3+\ox}{#4+\oy}{0} --
\iso{#3+\ox}{#4+\oy}{\scaledheight} --
\iso{#1+\ox}{#2+\oy}{\scaledheight} --
\iso{#1+\ox}{#2+\oy}{0} -- cycle;
% left side
\fill[orange!40]
\iso{#1-\ox}{#2-\oy}{0} --
\iso{#1-\ox}{#2-\oy}{\scaledheight} --
\iso{#3-\ox}{#4-\oy}{\scaledheight} --
\iso{#3-\ox}{#4-\oy}{0} -- cycle;
% front
\fill[orange!60]
\iso{#1-\ox}{#2-\oy}{0} --
\iso{#1-\ox}{#2-\oy}{\scaledheight} --
\iso{#1+\ox}{#2+\oy}{\scaledheight} --
\iso{#1+\ox}{#2+\oy}{0} -- cycle;
}
% path generation
\pgfmathsetmacro{\x}{0}
\pgfmathsetmacro{\y}{0}
\foreach \i in {1,...,\numexpr\LINESNUMBER-1\relax} {
\pgfmathparse{\y<0.001 ? \HEIGHTTOTAL : 0}
\let\ynew\pgfmathresult
\pgfmathsetmacro{\xnew}{\x+\xstep}
\filamentsegment{\x}{\y}{\x}{\ynew}
\filamentsegment{\x}{\ynew}{\xnew}{\ynew}
\xdef\x{\xnew}
\xdef\y{\ynew}
}
\pgfmathparse{\y<0.001 ? \HEIGHTTOTAL : 0}
\let\ynew\pgfmathresult
\filamentsegment{\x}{\y}{\x}{\ynew}
% AXES
\draw[ultra thick,red, -Stealth,line width=12pt]
\iso{0}{0}{0} -- \iso{20}{0}{0}
node[above,scale=5.8]{X};
\draw[ultra thick,green!70!black,-Stealth,line width=12pt]
\iso{0}{0}{0} -- \iso{0}{20}{0}
node[left,scale=5.8]{Y};
\draw[ultra thick,blue,-Stealth,line width=12pt]
\iso{0}{0}{0} -- \iso{0}{0}{10}
node[above,scale=5.8]{Z};
\end{scope}
\end{tikzpicture}
Модель параметризирована, можно менять количество дорожек, ширину и высоту дорожки, а также ширину и длину проверяемой рабочей зоны стола.
import FreeCAD
import PartDesign
import PartDesignGui
import Sketcher
import os
# PARAMETERS
EPS = 1E-3
SECTION_HEIGHT = 0.1 #[MM]
SECTION_WIDTH = 0.5 #[MM]
HEIGHT_TOTAL = 60 #[MM]
WIDTH_TOTAL = 60 #[MM]
LINES_NUMBER = 10 #minimum=2
x_step = WIDTH_TOTAL / (LINES_NUMBER - 1)
# MAKE DOCUMENT
App.newDocument().saveAs(os.path.join(os.curdir, "print_test.FCStd"))
document = App.activeDocument()
# SECTION SKETCH
document.addObject('PartDesign::Body','Body')
body = document.getObject('Body')
body.Label = 'Body'
body.newObject('Sketcher::SketchObject','SketchSection')
sketch_section = document.getObject('SketchSection')
sketch_section.AttachmentSupport = document.getObject('XZ_Plane')
sketch_section.MapMode = 'FlatFace'
document.recompute()
lastGeoId = len(sketch_section.Geometry)
geoList = []
geoList.append(Part.LineSegment(App.Vector(0, 0, 0.0),App.Vector(SECTION_WIDTH, 0, 0.0)))
geoList.append(Part.LineSegment(App.Vector(SECTION_WIDTH, 0, 0.0),App.Vector(SECTION_WIDTH, SECTION_HEIGHT, 0.0)))
geoList.append(Part.LineSegment(App.Vector(SECTION_WIDTH, SECTION_HEIGHT, 0.0),App.Vector(0, SECTION_HEIGHT, 0.0)))
geoList.append(Part.LineSegment(App.Vector(0, SECTION_HEIGHT, 0.0),App.Vector(0, 0, 0.0)))
document.recompute()
sketch_section.addGeometry(geoList,False)
# PATH SKETCH
document.addObject('Sketcher::SketchObject', 'SketchPath')
sketch_path = document.getObject('SketchPath')
sketch_path.AttachmentSupport = document.getObject('XY_Plane')
sketch_path.MapMode = "Deactivated"
document.recompute()
lastGeoId = len(sketch_path.Geometry)
geoList = []
x = 0
y = 0
for _ in range(LINES_NUMBER - 1):
y_new = HEIGHT_TOTAL if (y<EPS) else 0
x_new = x + x_step
geoList.append(Part.LineSegment(App.Vector(x, y , 0.0), App.Vector(x, y_new, 0.0)))
geoList.append(Part.LineSegment(App.Vector(x, y_new , 0.0), App.Vector(x_new, y_new, 0.0)))
y = y_new
x = x_new
y_new = HEIGHT_TOTAL if (y<EPS) else 0
geoList.append(Part.LineSegment(App.Vector(x, y , 0.0), App.Vector(x, y_new, 0.0)))
sketch_path.addGeometry(geoList,False)
document.recompute()
# ADDITIVE PIPE
body.newObject('PartDesign::AdditivePipe','AdditivePipe')
pipe = document.getObject('AdditivePipe')
pipe.Profile = document.getObject('SketchSection')
pipe.Spine = (document.getObject('SketchPath'),[])
pipe.Transition = u"Right corner"
document.recompute()
# EXPORT STL FILE
stl_path = os.path.join(os.path.abspath(os.curdir), "print_test.stl")
__objs__ = []
__objs__.append(body)
if hasattr(Mesh, "exportOptions"):
options = Mesh.exportOptions(stl_path)
Mesh.export(__objs__, stl_path, options)
else:
Mesh.export(__objs__, stl_path)
del __objs__
# SAVE AND CLOSE DOCUMENT
document.recompute()
document.save()
FreeCAD.closeDocument(document.Name)