From 2bf67f01910dd4d29076fd025471db45cf7e2b97 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 6 Sep 2021 15:27:52 +0200 Subject: [PATCH] Draft: Introduced Hatch command + object --- src/Mod/Draft/CMakeLists.txt | 4 + src/Mod/Draft/Draft.py | 5 + src/Mod/Draft/DraftTools.py | 1 + src/Mod/Draft/Resources/Draft.qrc | 2 + src/Mod/Draft/Resources/icons/Draft_Hatch.svg | 179 ++++++++++++++++++ src/Mod/Draft/Resources/ui/dialogHatch.ui | 89 +++++++++ src/Mod/Draft/draftguitools/gui_hatch.py | 132 +++++++++++++ src/Mod/Draft/draftmake/make_hatch.py | 48 +++++ src/Mod/Draft/draftobjects/hatch.py | 133 +++++++++++++ src/Mod/Draft/draftutils/init_tools.py | 2 +- .../Draft/draftviewproviders/view_hatch.py | 70 +++++++ 11 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Draft/Resources/icons/Draft_Hatch.svg create mode 100644 src/Mod/Draft/Resources/ui/dialogHatch.ui create mode 100644 src/Mod/Draft/draftguitools/gui_hatch.py create mode 100644 src/Mod/Draft/draftmake/make_hatch.py create mode 100644 src/Mod/Draft/draftobjects/hatch.py create mode 100644 src/Mod/Draft/draftviewproviders/view_hatch.py 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.svg icons/Snap_WorkingPlane.svg icons/Draft_NewLayer.svg + icons/Draft_Hatch.svg patterns/aluminium.svg patterns/brick01.svg patterns/concrete.svg @@ -184,5 +185,6 @@ ui/TaskShapeString.ui ui/dialog_AnnotationStyleEditor.ui ui/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 @@ + + + + TechDraw_TreeHatch + + + + image/svg+xml + + TechDraw_TreeHatch + + + [WandererFan] + + + TechDraw_TreeHatch + 2016-04-27 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_TreeHatch.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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)