# SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * * # * Copyright (c) 2024 Yorik van Havre * # * * # * This file is part of FreeCAD. * # * * # * FreeCAD is free software: you can redistribute it and/or modify it * # * under the terms of the GNU Lesser General Public License as * # * published by the Free Software Foundation, either version 2.1 of the * # * License, or (at your option) any later version. * # * * # * FreeCAD 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 * # * Lesser General Public License for more details. * # * * # * You should have received a copy of the GNU Lesser General Public * # * License along with FreeCAD. If not, see * # * . * # * * # *************************************************************************** """BIM Panel-related Arch_""" import FreeCAD import FreeCADGui QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate # Description l w t Presets = [None, ["Plywood 12mm, 1220 x 2440",1220,2440,12], ["Plywood 15mm, 1220 x 2440",1220,2440,15], ["Plywood 18mm, 1220 x 2440",1220,2440,18], ["Plywood 25mm, 1220 x 2440",1220,2440,25], ["MDF 3mm, 900 x 600", 900, 600, 3], ["MDF 6mm, 900 x 600", 900, 600, 6], ["OSB 18mm, 1220 x 2440", 1220,2440,18], ] class Arch_Panel: "the Arch Panel Arch_ definition" def GetResources(self): return {'Pixmap' : 'Arch_Panel', 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel","Panel"), 'Accel': "P, A", 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel","Creates a panel object from scratch or from a selected object (sketch, wire, face or solid)")} def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v def Activated(self): import WorkingPlane import Draft import draftguitools.gui_trackers as DraftTrackers from draftutils import params self.doc = FreeCAD.ActiveDocument self.Length = params.get_param_arch("PanelLength") self.Width = params.get_param_arch("PanelWidth") self.Thickness = params.get_param_arch("PanelThickness") self.Profile = None self.featureName = "Panel" self.rotated = False sel = FreeCADGui.Selection.getSelection() if sel: if len(sel) == 1: if Draft.getType(sel[0]) == "Panel": return self.doc.openTransaction(str(translate("Arch","Create Panel"))) FreeCADGui.addModule("Arch") FreeCADGui.addModule("Draft") for obj in sel: FreeCADGui.doCommand("obj = Arch.makePanel(FreeCAD.ActiveDocument." + obj.Name + ",thickness=" + str(self.Thickness) + ")") FreeCADGui.doCommand("Draft.autogroup(obj)") self.doc.commitTransaction() self.doc.recompute() return # interactive mode FreeCAD.activeDraftCommand = self # register as a Draft command for auto grid on/off WorkingPlane.get_working_plane() self.points = [] self.tracker = DraftTrackers.boxTracker() self.tracker.width(self.Width) self.tracker.height(self.Thickness) self.tracker.length(self.Length) self.tracker.on() FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox()) FreeCADGui.draftToolBar.continueCmd.show() def getPoint(self,point=None,obj=None): "this function is called by the snapper when it has a 3D point" import DraftVecUtils FreeCAD.activeDraftCommand = None FreeCADGui.Snapper.off() self.tracker.finalize() if point is None: return self.doc.openTransaction(translate("Arch","Create Panel")) FreeCADGui.addModule("Arch") if self.Profile: pr = Presets[self.Profile] FreeCADGui.doCommand('p = Arch.makeProfile('+str(pr[2])+','+str(pr[3])+','+str(pr[4])+','+str(pr[5])+')') FreeCADGui.doCommand('s = Arch.makePanel(p,thickness='+str(self.Thickness)+')') #FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)') else: FreeCADGui.doCommand('s = Arch.makePanel(length='+str(self.Length)+',width='+str(self.Width)+',thickness='+str(self.Thickness)+')') FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point)) if self.rotated: FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)') self.doc.commitTransaction() self.doc.recompute() if FreeCADGui.draftToolBar.continueCmd.isChecked(): self.Activated() def taskbox(self): "sets up a taskbox widget" from draftutils import params from PySide import QtCore, QtGui w = QtGui.QWidget() ui = FreeCADGui.UiLoader() w.setWindowTitle(translate("Arch","Panel options")) grid = QtGui.QGridLayout(w) # presets box labelp = QtGui.QLabel(translate("Arch","Preset")) valuep = QtGui.QComboBox() fpresets = [" "] for p in Presets[1:]: fpresets.append(translate("Arch",p[0])) valuep.addItems(fpresets) grid.addWidget(labelp,0,0,1,1) grid.addWidget(valuep,0,1,1,1) # length label1 = QtGui.QLabel(translate("Arch","Length")) self.vLength = ui.createWidget("Gui::InputField") self.vLength.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString) grid.addWidget(label1,1,0,1,1) grid.addWidget(self.vLength,1,1,1,1) # width label2 = QtGui.QLabel(translate("Arch","Width")) self.vWidth = ui.createWidget("Gui::InputField") self.vWidth.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString) grid.addWidget(label2,2,0,1,1) grid.addWidget(self.vWidth,2,1,1,1) # height label3 = QtGui.QLabel(translate("Arch","Thickness")) self.vHeight = ui.createWidget("Gui::InputField") self.vHeight.setText(FreeCAD.Units.Quantity(self.Thickness,FreeCAD.Units.Length).UserString) grid.addWidget(label3,3,0,1,1) grid.addWidget(self.vHeight,3,1,1,1) # horizontal button value4= QtGui.QPushButton(translate("Arch","Rotate")) grid.addWidget(value4,4,0,1,2) valuep.currentIndexChanged.connect(self.setPreset) self.vLength.valueChanged.connect(self.setLength) self.vWidth.valueChanged.connect(self.setWidth) self.vHeight.valueChanged.connect(self.setThickness) value4.pressed.connect(self.rotate) return w def update(self,point,info): "this function is called by the Snapper when the mouse is moved" if FreeCADGui.Control.activeDialog(): self.tracker.pos(point) if self.rotated: self.tracker.width(self.Thickness) self.tracker.height(self.Width) self.tracker.length(self.Length) else: self.tracker.width(self.Width) self.tracker.height(self.Thickness) self.tracker.length(self.Length) def setWidth(self,d): from draftutils import params self.Width = d.Value params.set_param_arch("PanelWidth",d) def setThickness(self,d): from draftutils import params self.Thickness = d.Value params.set_param_arch("PanelThickness",d) def setLength(self,d): from draftutils import params self.Length = d.Value params.set_param_arch("PanelLength",d) def setPreset(self,i): if i > 0: self.vLength.setText(FreeCAD.Units.Quantity(float(Presets[i][1]),FreeCAD.Units.Length).UserString) self.vWidth.setText(FreeCAD.Units.Quantity(float(Presets[i][2]),FreeCAD.Units.Length).UserString) self.vHeight.setText(FreeCAD.Units.Quantity(float(Presets[i][3]),FreeCAD.Units.Length).UserString) def rotate(self): self.rotated = not self.rotated class Arch_PanelCut: "the Arch Panel Cut command definition" def GetResources(self): return {'Pixmap' : 'Arch_Panel_Cut', 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Panel Cut"), 'Accel': "P, C", 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Creates 2D views of selected panels")} def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v def Activated(self): import Draft if FreeCADGui.Selection.getSelection(): FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Cut"))) FreeCADGui.addModule("Arch") for obj in FreeCADGui.Selection.getSelection(): if Draft.getType(obj) == "Panel": FreeCADGui.doCommand("Arch.makePanelCut(FreeCAD.ActiveDocument."+obj.Name+")") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class Arch_PanelSheet: "the Arch Panel Sheet definition" def GetResources(self): return {'Pixmap' : 'Arch_Panel_Sheet', 'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Panel Sheet"), 'Accel': "P, S", 'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates a 2D sheet which can contain panel cuts")} def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v def Activated(self): FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Sheet"))) FreeCADGui.addModule("Arch") if FreeCADGui.Selection.getSelection(): l = "[" for obj in FreeCADGui.Selection.getSelection(): l += "FreeCAD.ActiveDocument."+obj.Name+"," l += "]" FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() FreeCADGui.doCommand("__objs__ = "+l) FreeCADGui.doCommand("Arch.makePanelSheet(__objs__)") FreeCADGui.doCommand("del __objs__") else: FreeCADGui.doCommand("Arch.makePanelSheet()") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class Arch_Nest: "the Arch Panel definition" def GetResources(self): return {'Pixmap' : 'Arch_Nest', 'MenuText': QT_TRANSLATE_NOOP("Arch_Nest","Nest"), 'Accel': "N, E", 'ToolTip': QT_TRANSLATE_NOOP("Arch_Nest","Nests a series of selected shapes in a container")} def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v def Activated(self): FreeCADGui.Control.closeDialog() FreeCADGui.Control.showDialog(NestTaskPanel()) class NestTaskPanel: '''The TaskPanel for Arch Nest''' def __init__(self,obj=None): import ArchNesting self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchNest.ui") self.form.progressBar.hide() self.form.ButtonPreview.setEnabled(False) self.form.ButtonStop.setEnabled(False) self.form.ButtonContainer.pressed.connect(self.getContainer) self.form.ButtonShapes.pressed.connect(self.getShapes) self.form.ButtonRemove.pressed.connect(self.removeShapes) self.form.ButtonStart.pressed.connect(self.start) self.form.ButtonStop.pressed.connect(self.stop) self.form.ButtonPreview.pressed.connect(self.preview) self.shapes = [] self.container = None self.nester = None self.temps = [] def reject(self): self.stop() self.clearTemps() return True def accept(self): self.stop() self.clearTemps() if self.nester: FreeCAD.ActiveDocument.openTransaction("Nesting") self.nester.apply() FreeCAD.ActiveDocument.commitTransaction() return True def clearTemps(self): for t in self.temps: if FreeCAD.ActiveDocument.getObject(t.Name): FreeCAD.ActiveDocument.removeObject(t.Name) self.temps = [] def getContainer(self): import Draft s = FreeCADGui.Selection.getSelection() if len(s) == 1: if hasattr(s[0],'Shape'): if len(s[0].Shape.Faces) == 1: if not (s[0] in self.shapes): self.form.Container.clear() self.addObject(s[0],self.form.Container) self.container = s[0] else: FreeCAD.Console.PrintError(translate("Arch","This object has no face")) if Draft.getType(s[0]) == "PanelSheet": if hasattr(s[0],"Rotations"): if s[0].Rotations: self.form.Rotations.setText(str(s[0].Rotations)) def getShapes(self): s = FreeCADGui.Selection.getSelection() for o in s: if hasattr(o,'Shape'): if not o in self.shapes: if o != self.container: self.addObject(o,self.form.Shapes) self.shapes.append(o) def addObject(self,obj,form): from PySide import QtGui i = QtGui.QListWidgetItem() i.setText(obj.Label) i.setToolTip(obj.Name) if hasattr(obj.ViewObject,"Proxy"): if hasattr(obj.ViewObject.Proxy,"getIcon"): i.setIcon(QtGui.QIcon(obj.ViewObject.Proxy.getIcon())) elif hasattr(obj.ViewObject, "Icon"): i.setIcon(QtGui.QIcon(obj.ViewObject.Icon)) else: i.setIcon(QtGui.QIcon(":/icons/Part_3D_object.svg")) form.addItem(i) def removeShapes(self): for i in self.form.Shapes.selectedItems(): o = FreeCAD.ActiveDocument.getObject(i.toolTip()) if o: if o in self.shapes: self.shapes.remove(o) self.form.Shapes.takeItem(self.form.Shapes.row(i)) def setCounter(self,value): self.form.progressBar.setValue(value) def start(self): from PySide import QtGui import ArchNesting self.clearTemps() self.form.progressBar.setFormat("pass 1: %p%") self.form.progressBar.setValue(0) self.form.progressBar.show() tolerance = self.form.Tolerance.value() discretize = self.form.Subdivisions.value() rotations = [float(x) for x in self.form.Rotations.text().split(",")] ArchNesting.TOLERANCE = tolerance ArchNesting.DISCRETIZE = discretize ArchNesting.ROTATIONS = rotations self.nester = ArchNesting.Nester() self.nester.addContainer(self.container) self.nester.addObjects(self.shapes) self.nester.setCounter = self.setCounter self.form.ButtonStop.setEnabled(True) self.form.ButtonStart.setEnabled(False) self.form.ButtonPreview.setEnabled(False) QtGui.QApplication.processEvents() result = self.nester.run() self.form.progressBar.hide() self.form.ButtonStart.setEnabled(True) self.form.ButtonStop.setEnabled(False) if result: self.form.ButtonPreview.setEnabled(True) def stop(self): if self.nester: self.nester.stop() self.form.ButtonStart.setEnabled(True) self.form.ButtonStop.setEnabled(False) self.form.ButtonPreview.setEnabled(False) self.form.progressBar.hide() def preview(self): self.clearTemps() if self.nester: t = self.nester.show() if t: self.temps.extend(t) class Arch_PanelGroup: def GetCommands(self): return tuple(['Arch_Panel','Arch_Panel_Cut','Arch_Panel_Sheet','Arch_Nest']) def GetResources(self): return { 'MenuText': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel tools'), 'ToolTip': QT_TRANSLATE_NOOP("Arch_PanelTools",'Panel tools') } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v FreeCADGui.addCommand('Arch_Panel',Arch_Panel()) FreeCADGui.addCommand('Arch_Panel_Cut',Arch_PanelCut()) FreeCADGui.addCommand('Arch_Panel_Sheet',Arch_PanelSheet()) FreeCADGui.addCommand('Arch_Nest',Arch_Nest()) FreeCADGui.addCommand('Arch_PanelTools', Arch_PanelGroup())