Started GZ curves computation tool development

This commit is contained in:
Jose Luis Cercós pita 2012-05-22 11:10:55 +02:00
parent fbeedc1905
commit 3e34474d9b
8 changed files with 503 additions and 5 deletions

View File

@ -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}

View File

@ -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())

View File

@ -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)

View File

@ -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())

View File

@ -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]]

View File

@ -0,0 +1,233 @@
#***************************************************************************
#* *
#* Copyright (c) 2011, 2012 *
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
#* *
#* 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

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TaskPanel</class>
<widget class="QWidget" name="TaskPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<height>368</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>368</height>
</size>
</property>
<property name="windowTitle">
<string>GZ curve computation</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="LoadConditionGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Loading condition</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>231</width>
<height>151</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="Tanks">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="DisplacementLabel">
<property name="text">
<string>Displacement = 0 [kg]</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="DraftLabel">
<property name="text">
<string>Draft = 0 [m]</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="anglesBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Roll angles</string>
</property>
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>231</width>
<height>141</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="StartAngleLabel">
<property name="text">
<string>Start [deg]</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="EndAngleLabel">
<property name="text">
<string>End [deg]</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="NAngleLabel">
<property name="text">
<string>Number of points</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox"/>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_2"/>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinBox"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,36 @@
#***************************************************************************
#* *
#* Copyright (c) 2011, 2012 *
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
#* *
#* 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()