diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt
index 29ebf83e94..c21df3f9e7 100644
--- a/src/Mod/Draft/CMakeLists.txt
+++ b/src/Mod/Draft/CMakeLists.txt
@@ -137,6 +137,7 @@ SET(Draft_make_functions
draftmake/make_text.py
draftmake/make_wire.py
draftmake/make_wpproxy.py
+ draftmake/make_hatch.py
draftmake/README.md
)
@@ -169,6 +170,7 @@ SET(Draft_objects
draftobjects/text.py
draftobjects/wire.py
draftobjects/wpproxy.py
+ draftobjects/hatch.py
draftobjects/README.md
)
@@ -194,6 +196,7 @@ SET(Draft_view_providers
draftviewproviders/view_text.py
draftviewproviders/view_wire.py
draftviewproviders/view_wpproxy.py
+ draftviewproviders/view_hatch.py
draftviewproviders/README.md
)
@@ -213,6 +216,7 @@ SET(Creator_tools
draftguitools/gui_points.py
draftguitools/gui_facebinders.py
draftguitools/gui_labels.py
+ draftguitools/gui_hatch.py
)
SET(Modifier_tools
diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py
index 50251e6eb1..5287778e8e 100644
--- a/src/Mod/Draft/Draft.py
+++ b/src/Mod/Draft/Draft.py
@@ -422,4 +422,9 @@ if App.GuiUp:
from draftviewproviders.view_text import (ViewProviderText,
ViewProviderDraftText)
+from draftobjects.hatch import (Draft_Hatch_Object)
+from draftmake.make_hatch import (make_hatch, makeHatch)
+if App.GuiUp:
+ from draftviewproviders.view_hatch import (Draft_Hatch_ViewProvider)
+
## @}
diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py
index 2f43884416..d9e0df2f6a 100644
--- a/src/Mod/Draft/DraftTools.py
+++ b/src/Mod/Draft/DraftTools.py
@@ -168,6 +168,7 @@ from draftguitools.gui_shapestrings import ShapeString
from draftguitools.gui_points import Point
from draftguitools.gui_facebinders import Draft_Facebinder
from draftguitools.gui_labels import Draft_Label
+from draftguitools.gui_hatch import Draft_Hatch
# ---------------------------------------------------------------------------
# Modifier functions
diff --git a/src/Mod/Draft/Resources/Draft.qrc b/src/Mod/Draft/Resources/Draft.qrc
index 4bfa75d77a..7939d0b31a 100644
--- a/src/Mod/Draft/Resources/Draft.qrc
+++ b/src/Mod/Draft/Resources/Draft.qrc
@@ -107,6 +107,7 @@
icons/Snap_Special.svgicons/Snap_WorkingPlane.svgicons/Draft_NewLayer.svg
+ icons/Draft_Hatch.svgpatterns/aluminium.svgpatterns/brick01.svgpatterns/concrete.svg
@@ -184,5 +185,6 @@
ui/TaskShapeString.uiui/dialog_AnnotationStyleEditor.uiui/TaskPanel_SetStyle.ui
+ ui/dialogHatch.ui
diff --git a/src/Mod/Draft/Resources/icons/Draft_Hatch.svg b/src/Mod/Draft/Resources/icons/Draft_Hatch.svg
new file mode 100644
index 0000000000..520151fde7
--- /dev/null
+++ b/src/Mod/Draft/Resources/icons/Draft_Hatch.svg
@@ -0,0 +1,179 @@
+
+
diff --git a/src/Mod/Draft/Resources/ui/dialogHatch.ui b/src/Mod/Draft/Resources/ui/dialogHatch.ui
new file mode 100644
index 0000000000..e7ec28ccf1
--- /dev/null
+++ b/src/Mod/Draft/Resources/ui/dialogHatch.ui
@@ -0,0 +1,89 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 227
+ 142
+
+
+
+ Form
+
+
+
+
+
+
+
+
+ pattern files (*.pat)
+
+
+
+
+
+
+ PAT file:
+
+
+
+
+
+
+ Scale
+
+
+
+
+
+
+ Pattern:
+
+
+
+
+
+
+
+
+
+ 999999.000000000000000
+
+
+ 1000.000000000000000
+
+
+
+
+
+
+ Rotation:
+
+
+
+
+
+
+ °
+
+
+ 359.990000000000009
+
+
+
+
+
+
+
+ Gui::FileChooser
+ QWidget
+ Gui/FileDialog.h
+
+
+
+
+
diff --git a/src/Mod/Draft/draftguitools/gui_hatch.py b/src/Mod/Draft/draftguitools/gui_hatch.py
new file mode 100644
index 0000000000..a853188a47
--- /dev/null
+++ b/src/Mod/Draft/draftguitools/gui_hatch.py
@@ -0,0 +1,132 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2021 Yorik van Havre *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program is distributed in the hope that it will be useful, *
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+
+"""This module contains FreeCAD commands for the Draft workbench"""
+
+import os
+import FreeCAD
+from draftutils.translate import translate, QT_TRANSLATE_NOOP
+
+
+class Draft_Hatch:
+
+
+ def GetResources(self):
+
+ return {'Pixmap' : "Draft_Hatch",
+ 'MenuText': QT_TRANSLATE_NOOP("Draft_Hatch", "Hatch"),
+ 'Accel': "H, A",
+ 'ToolTip' : QT_TRANSLATE_NOOP("Draft_Hatch", "Create hatches on selected faces")}
+
+ def Activated(self):
+
+ import FreeCADGui
+
+ if FreeCADGui.Selection.getSelection():
+ FreeCADGui.Control.showDialog(Draft_Hatch_TaskPanel(FreeCADGui.Selection.getSelection()[0]))
+ else:
+ FreeCAD.Console.PrintError(translate("Draft","You must choose a base object before using this command")+"\n")
+
+
+class Draft_Hatch_TaskPanel:
+
+
+ def __init__(self,baseobj):
+
+ import FreeCADGui
+ from PySide import QtCore,QtGui
+ import Draft_rc
+
+ self.baseobj = baseobj
+ self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogHatch.ui")
+ self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_Hatch.svg"))
+ self.form.File.fileNameChanged.connect(self.onFileChanged)
+ self.p1 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/PAT")
+ self.p2 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
+ self.form.File.setFileName(self.p1.GetString("FilePattern",""))
+ pat = self.p1.GetString("NamePattern","")
+ if pat in [self.form.Pattern.itemText(i) for i in range(self.form.Pattern.count())]:
+ self.form.Pattern.setCurrentText(pat)
+ self.form.Scale.setValue(self.p2.GetFloat("HatchPatternScale",1000.0))
+ self.form.Rotation.setValue(self.p2.GetFloat("HatchPatternRotation",0.0))
+
+ def accept(self):
+
+ import FreeCADGui
+
+ self.p1.SetString("FilePattern",self.form.File.property("fileName"))
+ self.p1.SetString("NamePattern",self.form.Pattern.currentText())
+ self.p2.SetFloat("HatchPatternScale",self.form.Scale.value())
+ self.p2.SetFloat("HatchPatternRotation",self.form.Rotation.value())
+ if hasattr(self.baseobj,"File") and hasattr(self.baseobj,"Pattern"):
+ # modify existing hatch object
+ o = "FreeCAD.ActiveDocument.getObject(\""+self.baseobj.Name+"\")"
+ FreeCADGui.doCommand(o+".File=\""+self.form.File.property("fileName")+"\"")
+ FreeCADGui.doCommand(o+".Pattern=\""+self.form.Pattern.currentText()+"\"")
+ FreeCADGui.doCommand(o+".Scale="+str(self.form.Scale.value()))
+ FreeCADGui.doCommand(o+".Rotation="+str(self.form.Rotation.value()))
+ else:
+ # create new hatch object
+ FreeCAD.ActiveDocument.openTransaction("Create Hatch")
+ FreeCADGui.addModule("Draft")
+ cmd = "Draft.makeHatch("
+ cmd += "baseobject=FreeCAD.ActiveDocument.getObject(\""+self.baseobj.Name
+ cmd += "\"),filename=\""+self.form.File.property("fileName")
+ cmd += "\",pattern=\""+self.form.Pattern.currentText()
+ cmd += "\",scale="+str(self.form.Scale.value())
+ cmd += ",rotation="+str(self.form.Rotation.value())+")"
+ FreeCADGui.doCommand(cmd)
+ FreeCAD.ActiveDocument.commitTransaction()
+ FreeCADGui.doCommand("FreeCAD.ActiveDocument.recompute()")
+ self.reject()
+
+ def reject(self):
+
+ import FreeCADGui
+
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+
+ def onFileChanged(self,filename):
+
+ pat = self.form.Pattern.currentText()
+ self.form.Pattern.clear()
+ patterns = self.getPatterns(filename)
+ self.form.Pattern.addItems(patterns)
+ if pat in patterns:
+ self.form.Pattern.setCurrentText(pat)
+
+ def getPatterns(self,filename):
+
+ """returns a list of pattern names found in a PAT file"""
+ patterns = []
+ if os.path.exists(filename):
+ with open(filename) as patfile:
+ for line in patfile:
+ if line.startswith("*"):
+ patterns.append(line.split(",")[0][1:])
+ return patterns
+
+if FreeCAD.GuiUp:
+ import FreeCADGui
+ FreeCADGui.addCommand("Draft_Hatch",Draft_Hatch())
diff --git a/src/Mod/Draft/draftmake/make_hatch.py b/src/Mod/Draft/draftmake/make_hatch.py
new file mode 100644
index 0000000000..6188b88814
--- /dev/null
+++ b/src/Mod/Draft/draftmake/make_hatch.py
@@ -0,0 +1,48 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2021 Yorik van Havre *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program is distributed in the hope that it will be useful, *
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+"""This module contains FreeCAD commands for the Draft workbench"""
+
+import FreeCAD
+from draftobjects.hatch import Draft_Hatch_Object
+from draftviewproviders.view_hatch import Draft_Hatch_ViewProvider
+
+def makeHatch(baseobject,filename,pattern,scale,rotation):
+
+ """makeHatch(baseobject,filename,pattern,scale,rotation): Creates and returns a hatch
+ object made by applying the given pattern of the given PAT file to the faces of the
+ given base object. Given scale and rotation factors are applied to the hatch object.
+ The result is a Part-based object created inthe active document."""
+
+ if not FreeCAD.ActiveDocument:
+ return
+ obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Hatch")
+ Draft_Hatch_Object(obj)
+ obj.Base = baseobject
+ obj.File = filename
+ obj.Pattern = pattern
+ obj.Scale = scale
+ obj.Rotation = rotation
+ if FreeCAD.GuiUp:
+ Draft_Hatch_ViewProvider(obj.ViewObject)
+
+make_hatch = makeHatch
diff --git a/src/Mod/Draft/draftobjects/hatch.py b/src/Mod/Draft/draftobjects/hatch.py
new file mode 100644
index 0000000000..3e3eb3a8c3
--- /dev/null
+++ b/src/Mod/Draft/draftobjects/hatch.py
@@ -0,0 +1,133 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2021 Yorik van Havre *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program is distributed in the hope that it will be useful, *
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+
+"""This module contains FreeCAD commands for the Draft workbench"""
+
+import os
+import FreeCAD
+from draftutils.translate import translate, QT_TRANSLATE_NOOP
+
+
+
+
+class Draft_Hatch_Object:
+
+
+ def __init__(self,obj):
+
+ obj.Proxy = self
+ self.setProperties(obj)
+
+ def setProperties(self,obj):
+
+ pl = obj.PropertiesList
+ if not "Placement" in pl:
+ obj.addProperty("App::PropertyPlacement","Placement","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The placement of this object"))
+ if not "Shape" in pl:
+ obj.addProperty("Part::PropertyPartShape","Shape","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The shape of this object"))
+ if not "Base" in pl:
+ obj.addProperty("App::PropertyLink","Base","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The base object used by this object"))
+ if not "File" in pl:
+ obj.addProperty("App::PropertyFile","File","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The PAT file used by this object"))
+ if not "Pattern" in pl:
+ obj.addProperty("App::PropertyString","Pattern","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The pattern name used by this object"))
+ if not "Scale" in pl:
+ obj.addProperty("App::PropertyFloat","Scale","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The pattern scale used by this object"))
+ if not "Rotation" in pl:
+ obj.addProperty("App::PropertyAngle","Rotation","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","The pattern rotation used by this object"))
+ if not "Translate" in pl:
+ obj.addProperty("App::PropertyBool","Translate","Hatch",
+ QT_TRANSLATE_NOOP("App::Property","If set to False, hatch is applied as is to the faces, without translation (this might give wrong results for non-XY faces)"))
+ obj.Translate = True
+ self.Type = "Hatch"
+
+ def onDocumentRestored(self,obj):
+
+ self.setProperties(obj)
+
+ def __getstate__(self):
+
+ return None
+
+ def __setstate__(self,state):
+
+ return None
+
+ def execute(self,obj):
+
+ import Part
+ import TechDraw
+
+ if not obj.Base:
+ return
+ if not obj.File:
+ return
+ if not obj.Pattern:
+ return
+ if not obj.Scale:
+ return
+ if not obj.Pattern in self.getPatterns(obj.File):
+ return
+ if not obj.Base.isDerivedFrom("Part::Feature"):
+ return
+ if not obj.Base.Shape.Faces:
+ return
+
+ pla = obj.Placement
+ shapes = []
+ for face in obj.Base.Shape.Faces:
+ face = face.copy()
+ if obj.Translate:
+ bpoint = face.CenterOfMass
+ norm = face.normalAt(0,0)
+ fpla = FreeCAD.Placement(bpoint,FreeCAD.Rotation(FreeCAD.Vector(0,0,1),norm))
+ face.Placement = face.Placement.multiply(fpla.inverse())
+ if obj.Rotation:
+ face.rotate(FreeCAD.Vector(),FreeCAD.Vector(0,0,1),obj.Rotation)
+ shape = TechDraw.makeGeomHatch(face,obj.Scale,obj.Pattern,obj.File)
+ if obj.Rotation:
+ shape.rotate(FreeCAD.Vector(),FreeCAD.Vector(0,0,1),-obj.Rotation)
+ if obj.Translate:
+ shape.Placement = shape.Placement.multiply(fpla)
+ shapes.append(shape)
+ if shapes:
+ obj.Shape = Part.makeCompound(shapes)
+ obj.Placement = pla
+
+ def getPatterns(self,filename):
+
+ """returns a list of pattern names found in a PAT file"""
+ patterns = []
+ if os.path.exists(filename):
+ with open(filename) as patfile:
+ for line in patfile:
+ if line.startswith("*"):
+ patterns.append(line.split(",")[0][1:])
+ return patterns
diff --git a/src/Mod/Draft/draftutils/init_tools.py b/src/Mod/Draft/draftutils/init_tools.py
index e90cbe5ba4..edfccc3513 100644
--- a/src/Mod/Draft/draftutils/init_tools.py
+++ b/src/Mod/Draft/draftutils/init_tools.py
@@ -45,7 +45,7 @@ def get_draft_drawing_commands():
"Draft_Circle", "Draft_Ellipse", "Draft_Rectangle",
"Draft_Polygon", "Draft_BSpline", "Draft_BezierTools",
"Draft_Point", "Draft_Facebinder",
- "Draft_ShapeString"]
+ "Draft_ShapeString","Draft_Hatch"]
def get_draft_annotation_commands():
diff --git a/src/Mod/Draft/draftviewproviders/view_hatch.py b/src/Mod/Draft/draftviewproviders/view_hatch.py
new file mode 100644
index 0000000000..374cc06f3d
--- /dev/null
+++ b/src/Mod/Draft/draftviewproviders/view_hatch.py
@@ -0,0 +1,70 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2021 Yorik van Havre *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program is distributed in the hope that it will be useful, *
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* License along with this program; if not, write to the Free Software *
+#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+#* USA *
+#* *
+#***************************************************************************
+
+
+"""This module contains FreeCAD commands for the Draft workbench"""
+
+import os
+import FreeCAD
+from draftguitools.gui_hatch import Draft_Hatch_TaskPanel
+
+class Draft_Hatch_ViewProvider:
+
+
+ def __init__(self,vobj):
+
+ vobj.Proxy = self
+
+ def getIcon(self):
+
+ return ":/icons/Draft_Hatch.svg"
+
+ def __getstate__(self):
+
+ return None
+
+ def __setstate__(self,state):
+
+ return None
+
+ def setEdit(self,vobj,mode):
+
+ import FreeCADGui
+
+ taskd = Draft_Hatch_TaskPanel(vobj.Object)
+ taskd.form.File.setFileName(vobj.Object.File)
+ taskd.form.Pattern.setCurrentText(vobj.Object.Pattern)
+ taskd.form.Scale.setValue(vobj.Object.Scale)
+ taskd.form.Rotation.setValue(vobj.Object.Rotation)
+ FreeCADGui.Control.showDialog(taskd)
+ return True
+
+ def unsetEdit(self,vobj,mode):
+
+ import FreeCADGui
+
+ FreeCADGui.Control.closeDialog()
+ return True
+
+ def doubleClicked(self,vobj):
+
+ self.setEdit(vobj,None)