From 3e34474d9bad03987b56c5eec2f79ebd85998d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Cerc=C3=B3s=20pita?= Date: Tue, 22 May 2012 11:10:55 +0200 Subject: [PATCH] Started GZ curves computation tool development --- src/Mod/Ship/CMakeLists.txt | 15 +- src/Mod/Ship/InitGui.py | 4 +- src/Mod/Ship/Makefile.am | 5 +- src/Mod/Ship/ShipGui.py | 13 ++ src/Mod/Ship/TankInstance.py | 62 +++++++- src/Mod/Ship/tankGZ/TaskPanel.py | 233 +++++++++++++++++++++++++++++++ src/Mod/Ship/tankGZ/TaskPanel.ui | 140 +++++++++++++++++++ src/Mod/Ship/tankGZ/__init__.py | 36 +++++ 8 files changed, 503 insertions(+), 5 deletions(-) create mode 100644 src/Mod/Ship/tankGZ/TaskPanel.py create mode 100644 src/Mod/Ship/tankGZ/TaskPanel.ui create mode 100644 src/Mod/Ship/tankGZ/__init__.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index c44e313ab..3106dbc47 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -114,7 +114,14 @@ SET(ShipCreateTank_SRCS ) SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) -SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS}) +SET(ShipGZ_SRCS + tankGZ/__init__.py + tankGZ/TaskPanel.py + tankGZ/TaskPanel.ui +) +SOURCE_GROUP("shipcreatetank" FILES ${ShipCreateTank_SRCS}) + +SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS} ${ShipGZ_SRCS}) ADD_CUSTOM_TARGET(Ship ALL SOURCES ${all_files} @@ -182,6 +189,12 @@ INSTALL( DESTINATION Mod/Ship/tankCreateTank ) +INSTALL( + FILES + ${ShipGZ_SRCS} + DESTINATION + Mod/Ship/tankGZ +) INSTALL( FILES ${ShipMain_SRCS} diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index 6cf4c9689..aa8a409af 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -34,13 +34,13 @@ class ShipWorkbench ( Workbench ): # ToolBar list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendToolbar("Ship design",list) - list = ["Ship_Weights", "Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank", "Ship_GZ"] self.appendToolbar("Loading",list) # Menu list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"] self.appendMenu("Ship design",list) - list = ["Ship_Weights", "Ship_CreateTank"] + list = ["Ship_Weights", "Ship_CreateTank", "Ship_GZ"] self.appendToolbar("Loading",list) Gui.addWorkbench(ShipWorkbench()) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index 01b296c01..54b7ecf6a 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -75,7 +75,10 @@ nobase_data_DATA = \ tankWeights/TaskPanel.ui \ tankCreateTank/__init__.py \ tankCreateTank/TaskPanel.py \ - tankCreateTank/TaskPanel.ui + tankCreateTank/TaskPanel.ui \ + tankGZ/__init__.py \ + tankGZ/TaskPanel.py \ + tankGZ/TaskPanel.ui CLEANFILES = $(BUILT_SOURCES) diff --git a/src/Mod/Ship/ShipGui.py b/src/Mod/Ship/ShipGui.py index 71a9e75e4..c97f1e908 100644 --- a/src/Mod/Ship/ShipGui.py +++ b/src/Mod/Ship/ShipGui.py @@ -108,6 +108,18 @@ class CreateTank: ToolTip = str(Translator.translate('Create a new ship tank')) return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} +class GZ: + def Activated(self): + import tankGZ + tankGZ.load() + + def GetResources(self): + from shipUtils import Paths, Translator + IconPath = Paths.iconsPath() + "/HydrostaticsIco.png" + MenuText = str(Translator.translate('GZ curve')) + ToolTip = str(Translator.translate('Transversal stability GZ curve computation')) + return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} + FreeCADGui.addCommand('Ship_LoadExample', LoadExample()) FreeCADGui.addCommand('Ship_CreateShip', CreateShip()) FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw()) @@ -115,3 +127,4 @@ FreeCADGui.addCommand('Ship_AreasCurve', AreasCurve()) FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics()) FreeCADGui.addCommand('Ship_Weights', SetWeights()) FreeCADGui.addCommand('Ship_CreateTank', CreateTank()) +FreeCADGui.addCommand('Ship_GZ', GZ()) diff --git a/src/Mod/Ship/TankInstance.py b/src/Mod/Ship/TankInstance.py index 584aa0f97..6e44795f8 100644 --- a/src/Mod/Ship/TankInstance.py +++ b/src/Mod/Ship/TankInstance.py @@ -45,7 +45,7 @@ class ShipTank: # Add uniqueness property to identify Tank instances obj.addProperty("App::PropertyBool","IsShipTank","ShipTank", str(Translator.translate("True if is a valid ship tank instance"))).IsShipTank=True # Add general options - obj.addProperty("App::PropertyFloat","Level","ShipTank", str(Translator.translate("Filling level"))).Level=level + obj.addProperty("App::PropertyFloat","Level","ShipTank", str(Translator.translate("Fluid filling level percentage"))).Level=level obj.addProperty("App::PropertyFloat","Density","ShipTank", str(Translator.translate("Inside fluid density"))).Density=density # Add shapes shape = self.computeShape(solid) @@ -1884,3 +1884,63 @@ class ViewProviderShipTank: " ", " "}; """ + +def tankWeight(obj, angles=Vector(0.0,0.0,0.0), cor=Vector(0.0,0.0,0.0)): + """ Compute tank fluid weight and their center of gravity. + @param obj Tank object. + @param angles Tank angles, Roll, Pitch and Yaw. + @param cor Center or rotation. + @return Weight and center of gravity. None if errors detected + """ + # Test if is a tank instance + props = obj.PropertiesList + try: + props.index("IsShipTank") + except ValueError: + return None + if not obj.IsShipTank: + return None + # Get object solids + Solids = obj.Shape.Solids + W = [0.0, 0.0, 0.0, 0.0] + for s in Solids: + # Get fluid volume + bbox = s.BoundBox + z0 = bbox.ZMin + z1 = bbox.ZMax + dz = obj.Level/100.0 * (z1-z0) + z = z0 + dz + dx = bbox.XMax-bbox.XMin + dy = bbox.YMax-bbox.YMin + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + vol = fluid.Volume + W[0] = W[0] + vol*obj.Density + # Compute fluid solid in rotated position (non linear rotation + # are ussually computed as Roll -> Pitch -> Yaw). + s.rotate(cor, Vector(1.0,0.0,0.0), angles.x) + s.rotate(cor, Vector(0.0,1.0,0.0), angles.y) + s.rotate(cor, Vector(0.0,0.0,1.0), angles.z) + bbox = s.BoundBox + z0 = bbox.ZMin + z1 = bbox.ZMax + dx = bbox.XMax-bbox.XMin + dy = bbox.YMax-bbox.YMin + Error = 0.01*vol + z = 0.0 + v = 0.0 + while(abs(vol - v) > Error): + z = z + (vol - v) / (dx*dy) + dz = z - z0 + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + v = fluid.Volume + if(abs(vol - v) / (dx*dy) <= 0.000001): + break + # Add fluid moments + for f in fluid.Solids: + cog = f.CenterOfMass + W[1] = W[1] + f.Volume*obj.Density*cog.x + W[2] = W[2] + f.Volume*obj.Density*cog.y + W[3] = W[3] + f.Volume*obj.Density*cog.z + return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] diff --git a/src/Mod/Ship/tankGZ/TaskPanel.py b/src/Mod/Ship/tankGZ/TaskPanel.py new file mode 100644 index 000000000..8a47e67ae --- /dev/null +++ b/src/Mod/Ship/tankGZ/TaskPanel.py @@ -0,0 +1,233 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* 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 * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD as App +import FreeCADGui as Gui +# Qt library +from PyQt4 import QtGui,QtCore +# Module +from Instance import * +from TankInstance import * +from shipUtils import Paths, Translator + +class TaskPanel: + def __init__(self): + self.ui = Paths.modulePath() + "/tankGZ/TaskPanel.ui" + self.ship = None + self.tanks = {} + + def accept(self): + if not self.ship: + return False + return True + + def reject(self): + if not self.ship: + return False + return True + + def clicked(self, index): + pass + + def open(self): + pass + + def needsFullSpace(self): + return True + + def isAllowedAlterSelection(self): + return False + + def isAllowedAlterView(self): + return True + + def isAllowedAlterDocument(self): + return False + + def helpRequested(self): + pass + + def setupUi(self): + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.tanks = form.findChild(QtGui.QListWidget, "Tanks") + form.disp = form.findChild(QtGui.QLabel, "DisplacementLabel") + form.draft = form.findChild(QtGui.QLabel, "DraftLabel") + self.form = form + # Initial values + if self.initValues(): + return True + self.retranslateUi() + self.onTanksSelection() + # Connect Signals and Slots + QtCore.QObject.connect(form.tanks,QtCore.SIGNAL("itemSelectionChanged()"),self.onTanksSelection) + return False + + def getMainWindow(self): + "returns the main window" + # using QtGui.qApp.activeWindow() isn't very reliable because if another + # widget than the mainwindow is active (e.g. a dialog) the wrong widget is + # returned + toplevel = QtGui.qApp.topLevelWidgets() + for i in toplevel: + if i.metaObject().className() == "Gui::MainWindow": + return i + raise Exception("No main window found") + + def initValues(self): + """ Get selected geometry. + @return False if sucessfully values initialized. + """ + # Get selected objects + selObjs = FreeCADGui.Selection.getSelection() + if not selObjs: + msg = Translator.translate("Ship instance must be selected (no object selected)\n") + App.Console.PrintError(msg) + return True + for i in range(0,len(selObjs)): + obj = selObjs[i] + # Test if is a ship instance + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + continue + if obj.IsShip: + # Test if another ship already selected + if self.ship: + msg = Translator.translate("More than one ship selected (extra ship will be neglected)\n") + App.Console.PrintWarning(msg) + break + self.ship = obj + # Test if any valid ship was selected + if not self.ship: + msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n") + App.Console.PrintError(msg) + return True + props = self.ship.PropertiesList + try: + props.index("WeightNames") + except: + msg = Translator.translate("Ship weights has not been set. You need to set weights before use this tool.\n") + App.Console.PrintError(msg) + return True + # Setup available tanks list + objs = App.ActiveDocument.Objects + iconPath = Paths.iconsPath() + "/Tank.xpm" + icon = QtGui.QIcon(QtGui.QPixmap(iconPath)) + for obj in objs: + # Try to get valid tank property + props = obj.PropertiesList + try: + props.index("IsShipTank") + except ValueError: + continue + if not obj.IsShipTank: + continue + # Add tank to list + name = obj.Name + label = obj.Label + tag = label + ' (' + name + ')' + self.tanks[tag] = name + # self.tanks.append([name, tag]) + item = QtGui.QListWidgetItem(tag) + item.setIcon(icon) + self.form.tanks.addItem(item) + msg = Translator.translate("Ready to work\n") + App.Console.PrintMessage(msg) + return False + + def retranslateUi(self): + """ Set user interface locale strings. + """ + self.form.setWindowTitle(Translator.translate("GZ curve computation")) + self.form.findChild(QtGui.QGroupBox, "LoadConditionGroup").setTitle(Translator.translate("Loading condition.")) + + def onTanksSelection(self): + """ Called when tanks are selected or deselected. + """ + # Set displacement label + disp = self.computeDisplacement() + self.form.disp.setText(Translator.translate("Displacement") + ' %g [kg]' % (disp[0])) + + def getTanks(self): + """ Get the selected tanks objects list. + @return Selected tanks list. + """ + items = self.form.tanks.selectedItems() + tanks = [] + for item in items: + tag = str(item.text()) + name = self.tanks[tag] + t = App.ActiveDocument.getObject('Tank') + if not t: + continue + tanks.append(t) + return tanks + + def computeDisplacement(self): + """ Computes ship displacement. + @return Ship displacement and center of gravity. None if errors detected. + """ + if not self.ship: + return None + # Test if is a ship instance + obj = self.ship + props = obj.PropertiesList + try: + props.index("IsShip") + except ValueError: + return None + if not obj.IsShip: + return None + # Test if properties already exist + try: + props.index("WeightNames") + except: + return None + # Get ship structure weights + W = [0.0, 0.0, 0.0, 0.0] + sWeights = weights(obj) + for w in sWeights: + W[0] = W[0] + w[1] + W[1] = W[1] + w[1]*w[2][0] + W[2] = W[2] + w[1]*w[2][1] + W[3] = W[3] + w[1]*w[2][2] + # Get selected tanks weights + tanks = self.getTanks() + for t in tanks: + w = tankWeight(t) + W[0] = W[0] + w[0] + W[1] = W[1] + w[0]*w[1] + W[2] = W[2] + w[0]*w[2] + W[3] = W[3] + w[0]*w[3] + return [W[0], W[1]/W[0], W[2]/W[0], W[3]/W[0]] + +def createTask(): + panel = TaskPanel() + Gui.Control.showDialog(panel) + if panel.setupUi(): + Gui.Control.closeDialog(panel) + return None + return panel diff --git a/src/Mod/Ship/tankGZ/TaskPanel.ui b/src/Mod/Ship/tankGZ/TaskPanel.ui new file mode 100644 index 000000000..a1f16c39b --- /dev/null +++ b/src/Mod/Ship/tankGZ/TaskPanel.ui @@ -0,0 +1,140 @@ + + + TaskPanel + + + + 0 + 0 + 256 + 368 + + + + + 256 + 368 + + + + GZ curve computation + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Loading condition + + + + + 0 + 20 + 231 + 151 + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::MultiSelection + + + + + + + Displacement = 0 [kg] + + + + + + + Draft = 0 [m] + + + + + + + + + + + + 0 + 0 + + + + Roll angles + + + + + 0 + 20 + 231 + 141 + + + + + QLayout::SetMinimumSize + + + + + Start [deg] + + + + + + + End [deg] + + + + + + + Number of points + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Ship/tankGZ/__init__.py b/src/Mod/Ship/tankGZ/__init__.py new file mode 100644 index 000000000..cbfb57d75 --- /dev/null +++ b/src/Mod/Ship/tankGZ/__init__.py @@ -0,0 +1,36 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* 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 * +#* * +#*************************************************************************** + +# FreeCAD modules +import FreeCAD +import FreeCADGui + +# Qt libraries +from PyQt4 import QtGui,QtCore + +# Main object +import TaskPanel + +def load(): + """ Loads the tool """ + TaskPanel.createTask()