Started the GZ tool

This commit is contained in:
Jose Luis Cercos Pita 2015-10-13 18:39:19 +02:00
parent 925b861fc0
commit d63348acf9
6 changed files with 610 additions and 6 deletions

View File

@ -80,6 +80,14 @@ SET(ShipCapacityCurve_SRCS
)
SOURCE_GROUP("shipcapacitycurve" FILES ${ShipCapacityCurve_SRCS})
SET(ShipGZ_SRCS
shipGZ/__init__.py
shipGZ/PlotAux.py
shipGZ/TaskPanel.py
shipGZ/TaskPanel.ui
)
SOURCE_GROUP("shipgz" FILES ${ShipGZ_SRCS})
SET(ShipUtils_SRCS
shipUtils/__init__.py
shipUtils/Locale.py
@ -89,7 +97,7 @@ SET(ShipUtils_SRCS
)
SOURCE_GROUP("shiputils" FILES ${ShipUtils_SRCS})
SET(all_files ${ShipMain_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipCreateWeight_SRCS} ${ShipCreateTank_SRCS} ${ShipCapacityCurve_SRCS} ${ShipUtils_SRCS})
SET(all_files ${ShipMain_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipCreateWeight_SRCS} ${ShipCreateTank_SRCS} ${ShipCapacityCurve_SRCS} ${ShipGZ_SRCS} ${ShipUtils_SRCS})
ADD_CUSTOM_TARGET(Ship ALL
SOURCES ${all_files} ${Ship_QRC_SRCS}
@ -156,6 +164,12 @@ INSTALL(
DESTINATION
Mod/Ship/shipCapacityCurve
)
INSTALL(
FILES
${ShipGZ_SRCS}
DESTINATION
Mod/Ship/shipGZ
)
INSTALL(
FILES
${ShipUtils_SRCS}

View File

@ -52,12 +52,9 @@ class ShipWorkbench(Workbench):
"Ship_Hydrostatics"]
weightslist = ["Ship_Weight",
"Ship_Tank",
"Ship_Capacity"]
"""
weightslist = ["Ship_Weights",
"Ship_CreateTank",
"Ship_Capacity",
"Ship_GZ"]
"""
self.appendToolbar(
str(QtCore.QT_TRANSLATE_NOOP("Ship", "Ship design")),
shiplist)

View File

@ -170,6 +170,23 @@ class TankCapacity:
'ToolTip': ToolTip}
class GZ:
def Activated(self):
import shipGZ
shipGZ.load()
def GetResources(self):
MenuText = QtCore.QT_TRANSLATE_NOOP(
'ship_gz',
'GZ curve computation')
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'ship_gz',
'Plot the GZ curve')
return {'Pixmap': 'Ship_GZ',
'MenuText': MenuText,
'ToolTip': ToolTip}
FreeCADGui.addCommand('Ship_LoadExample', LoadExample())
FreeCADGui.addCommand('Ship_CreateShip', CreateShip())
FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw())
@ -178,3 +195,4 @@ FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics())
FreeCADGui.addCommand('Ship_Weight', CreateWeight())
FreeCADGui.addCommand('Ship_Tank', CreateTank())
FreeCADGui.addCommand('Ship_Capacity', TankCapacity())
FreeCADGui.addCommand('Ship_GZ', GZ())

View File

@ -0,0 +1,149 @@
#***************************************************************************
#* *
#* 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 *
#* *
#***************************************************************************
import os
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
from FreeCAD import Base
import Spreadsheet
class Plot(object):
def __init__(self, x, y, disp, xcb, ship):
""" Constructor. performs the plot and shows it.
@param x X coordinates.
@param y Transversal computed areas.
@param disp Ship displacement.
@param xcb Bouyancy center length.
@param ship Active ship instance.
"""
self.plot(x, y, disp, xcb, ship)
self.spreadSheet(x, y, ship)
def plot(self, x, y, disp, xcb, ship):
""" Perform the areas curve plot.
@param x X coordinates.
@param y Transversal areas.
@param disp Ship displacement.
@param xcb Bouyancy center length.
@param ship Active ship instance.
@return True if error happens.
"""
try:
import Plot
plt = Plot.figure('Areas curve')
except ImportError:
msg = QtGui.QApplication.translate(
"ship_console",
"Plot module is disabled, so I cannot perform the plot",
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintWarning(msg + '\n')
return True
# Plot areas curve
areas = Plot.plot(x, y, 'Transversal areas')
areas.line.set_linestyle('-')
areas.line.set_linewidth(2.0)
areas.line.set_color((0.0, 0.0, 0.0))
# Get perpendiculars data
Lpp = ship.Length.getValueAs('m').Value
FPx = 0.5 * Lpp
APx = -0.5 * Lpp
maxArea = max(y)
# Plot perpendiculars
FP = Plot.plot([FPx, FPx], [0.0, maxArea])
FP.line.set_linestyle('-')
FP.line.set_linewidth(1.0)
FP.line.set_color((0.0, 0.0, 0.0))
AP = Plot.plot([APx, APx], [0.0, maxArea])
AP.line.set_linestyle('-')
AP.line.set_linewidth(1.0)
AP.line.set_color((0.0, 0.0, 0.0))
# Add annotations for prependiculars
ax = Plot.axes()
ax.annotate('AP', xy=(APx + 0.01 * Lpp, 0.01 * maxArea), size=15)
ax.annotate('AP', xy=(APx + 0.01 * Lpp, 0.95 * maxArea), size=15)
ax.annotate('FP', xy=(FPx + 0.01 * Lpp, 0.01 * maxArea), size=15)
ax.annotate('FP', xy=(FPx + 0.01 * Lpp, 0.95 * maxArea), size=15)
# Add some additional data
addInfo = ("$XCB = {0} \\; \\mathrm{{m}}$\n"
"$Area_{{max}} = {1} \\; \\mathrm{{m}}^2$\n"
"$\\bigtriangleup = {2} \\; \\mathrm{{tons}}$".format(
xcb,
maxArea,
disp))
ax.text(0.0,
0.01 * maxArea,
addInfo,
verticalalignment='bottom',
horizontalalignment='center',
fontsize=20)
# Write axes titles
Plot.xlabel(r'$x \; \mathrm{m}$')
Plot.ylabel(r'$Area \; \mathrm{m}^2$')
ax.xaxis.label.set_fontsize(20)
ax.yaxis.label.set_fontsize(20)
# Show grid
Plot.grid(True)
# End
plt.update()
return False
def spreadSheet(self, x, y, ship):
""" Write the output data file.
@param x X coordinates.
@param y Transversal areas.
@param ship Active ship instance.
"""
s = FreeCAD.activeDocument().addObject('Spreadsheet::Sheet',
'Areas curve')
# Print the header
s.set("A1", "x [m]")
s.set("B1", "area [m^2]")
s.set("C1", "FP x")
s.set("D1", "FP y")
s.set("E1", "AP x")
s.set("F1", "AP y")
# Print the perpendiculars data
Lpp = ship.Length.getValueAs('m').Value
FPx = 0.5 * Lpp
APx = -0.5 * Lpp
maxArea = max(y)
s.set("C2", str(FPx))
s.set("D2", str(0.0))
s.set("C3", str(FPx))
s.set("D3", str(maxArea))
s.set("E2", str(APx))
s.set("F2", str(0.0))
s.set("E3", str(APx))
s.set("F3", str(maxArea))
# Print the data
for i in range(len(x)):
s.set("A{}".format(i + 2), str(x[i]))
s.set("B{}".format(i + 2), str(y[i]))
# Recompute
FreeCAD.activeDocument().recompute()

View File

@ -0,0 +1,397 @@
#***************************************************************************
#* *
#* 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 *
#* *
#***************************************************************************
import math
import FreeCAD as App
import FreeCADGui as Gui
import Units
from PySide import QtGui, QtCore
import Preview
import PlotAux
import Instance
from shipUtils import Paths
import shipUtils.Units as USys
import shipUtils.Locale as Locale
from shipHydrostatics import Tools as Hydrostatics
class TaskPanel:
def __init__(self):
self.ui = Paths.modulePath() + "/shipAreasCurve/TaskPanel.ui"
self.preview = Preview.Preview()
self.ship = None
def accept(self):
if not self.ship:
return False
self.save()
# Plot data
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
draft = Units.Quantity(Locale.fromString(
form.draft.text())).getValueAs('m').Value
trim = Units.Quantity(Locale.fromString(
form.trim.text())).getValueAs('deg').Value
data = Hydrostatics.displacement(self.ship,
draft,
0.0,
trim)
disp = data[0]
xcb = data[1].x
data = Hydrostatics.areas(self.ship,
draft,
0.0,
trim)
x = []
y = []
for i in range(0, len(data)):
x.append(data[i][0])
y.append(data[i][1])
PlotAux.Plot(x, y, disp, xcb, self.ship)
self.preview.clean()
return True
def reject(self):
self.preview.clean()
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.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
form.output = self.widget(QtGui.QTextEdit, "OutputData")
form.doc = QtGui.QTextDocument(form.output)
self.form = form
if self.initValues():
return True
self.retranslateUi()
QtCore.QObject.connect(form.draft,
QtCore.SIGNAL("valueChanged(double)"),
self.onData)
QtCore.QObject.connect(form.trim,
QtCore.SIGNAL("valueChanged(double)"),
self.onData)
def getMainWindow(self):
toplevel = QtGui.qApp.topLevelWidgets()
for i in toplevel:
if i.metaObject().className() == "Gui::MainWindow":
return i
raise RuntimeError("No main window found")
def widget(self, class_id, name):
"""Return the selected widget.
Keyword arguments:
class_id -- Class identifier
name -- Name of the widget
"""
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
return form.findChild(class_id, name)
def initValues(self):
""" Set initial values for fields
"""
selObjs = Gui.Selection.getSelection()
if not selObjs:
msg = QtGui.QApplication.translate(
"ship_console",
"A ship instance must be selected before using this tool (no"
" objects selected)",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
return True
for i in range(0, len(selObjs)):
obj = selObjs[i]
props = obj.PropertiesList
try:
props.index("IsShip")
except ValueError:
continue
if obj.IsShip:
if self.ship:
msg = QtGui.QApplication.translate(
"ship_console",
"More than one ship have been selected (the extra"
" ships will be ignored)",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintWarning(msg + '\n')
break
self.ship = obj
if not self.ship:
msg = QtGui.QApplication.translate(
"ship_console",
"A ship instance must be selected before using this tool (no"
" valid ship found at the selected objects)",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
return True
length_format = USys.getLengthFormat()
angle_format = USys.getAngleFormat()
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
form.draft.setText(Locale.toString(length_format.format(
self.ship.Draft.getValueAs(USys.getLengthUnits()).Value)))
form.trim.setText(Locale.toString(angle_format.format(0.0)))
# Try to use saved values
props = self.ship.PropertiesList
try:
props.index("AreaCurveDraft")
form.draft.setText(Locale.toString(length_format.format(
self.ship.AreaCurveDraft.getValueAs(
USys.getLengthUnits()).Value)))
except:
pass
try:
props.index("AreaCurveTrim")
form.trim.setText(Locale.toString(angle_format.format(
self.ship.AreaCurveTrim.getValueAs(
USys.getAngleUnits()).Value)))
except ValueError:
pass
# Update GUI
draft = Units.Quantity(form.draft.text()).getValueAs('m').Value
trim = Units.Quantity(form.trim.text()).getValueAs('deg').Value
self.preview.update(draft, trim, self.ship)
self.onUpdate()
return False
def retranslateUi(self):
""" Set user interface locale strings. """
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.setWindowTitle(QtGui.QApplication.translate(
"ship_areas",
"Plot the transversal areas curve",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "DraftLabel").setText(
QtGui.QApplication.translate(
"ship_areas",
"Draft",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "TrimLabel").setText(
QtGui.QApplication.translate(
"ship_areas",
"Trim",
None,
QtGui.QApplication.UnicodeUTF8))
def clampLength(self, widget, val_min, val_max, val):
if val >= val_min and val <= val_max:
return val
input_format = USys.getLengthFormat()
val = min(val_max, max(val_min, val))
qty = Units.Quantity('{} m'.format(val))
widget.setText(Locale.toString(input_format.format(
qty.getValueAs(USys.getLengthUnits()).Value)))
return val
def clampAngle(self, widget, val_min, val_max, val):
if val >= val_min and val <= val_max:
return val
input_format = USys.getAngleFormat()
val = min(val_max, max(val_min, val))
qty = Units.Quantity('{} deg'.format(val))
widget.setText(Locale.toString(input_format.format(
qty.getValueAs(USys.getLengthUnits()).Value)))
return val
def onData(self, value):
""" Method called when the tool input data is touched.
@param value Changed value.
"""
if not self.ship:
return
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
# Get the values (or fix them in bad setting case)
try:
draft = Units.Quantity(Locale.fromString(
form.draft.text())).getValueAs('m').Value
except:
draft = self.ship.Draft.getValueAs(USys.getLengthUnits()).Value
input_format = USys.getLengthFormat()
qty = Units.Quantity('{} m'.format(draft))
widget.setText(Locale.toString(input_format.format(
qty.getValueAs(USys.getLengthUnits()).Value)))
try:
trim = Units.Quantity(Locale.fromString(
form.trim.text())).getValueAs('deg').Value
except:
trim = 0.0
input_format = USys.getAngleFormat()
qty = Units.Quantity('{} deg'.format(trim))
widget.setText(Locale.toString(input_format.format(
qty.getValueAs(USys.getLengthUnits()).Value)))
bbox = self.ship.Shape.BoundBox
draft_min = bbox.ZMin / Units.Metre.Value
draft_max = bbox.ZMax / Units.Metre.Value
draft = self.clampLength(form.draft, draft_min, draft_max, draft)
trim_min = -180.0
trim_max = 180.0
trim = self.clampAngle(form.trim, trim_min, trim_max, trim)
self.onUpdate()
self.preview.update(draft, trim, self.ship)
def onUpdate(self):
""" Method called when the data update is requested. """
if not self.ship:
return
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
form.output = self.widget(QtGui.QTextEdit, "OutputData")
draft = Units.Quantity(Locale.fromString(
form.draft.text())).getValueAs('m').Value
trim = Units.Quantity(Locale.fromString(
form.trim.text())).getValueAs('deg').Value
# Calculate the drafts at each perpendicular
angle = math.radians(trim)
L = self.ship.Length.getValueAs('m').Value
B = self.ship.Breadth.getValueAs('m').Value
draftAP = draft + 0.5 * L * math.tan(angle)
if draftAP < 0.0:
draftAP = 0.0
draftFP = draft - 0.5 * L * math.tan(angle)
if draftFP < 0.0:
draftFP = 0.0
# Calculate the involved hydrostatics
data = Hydrostatics.displacement(self.ship,
draft,
0.0,
trim)
# Setup the html string
string = 'L = {0} [m]<BR>'.format(L)
string = string + 'B = {0} [m]<BR>'.format(B)
string = string + 'T = {0} [m]<HR>'.format(draft)
string = string + 'Trim = {0} [degrees]<BR>'.format(trim)
string = string + 'T<sub>AP</sub> = {0} [m]<BR>'.format(draftAP)
string = string + 'T<sub>FP</sub> = {0} [m]<HR>'.format(draftFP)
dispText = QtGui.QApplication.translate(
"ship_areas",
'Displacement',
None,
QtGui.QApplication.UnicodeUTF8)
string = string + dispText + ' = {0} [ton]<BR>'.format(data[0])
string = string + 'XCB = {0} [m]'.format(data[1].x)
form.output.setHtml(string)
def save(self):
""" Saves the data into ship instance. """
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.draft = self.widget(QtGui.QLineEdit, "Draft")
form.trim = self.widget(QtGui.QLineEdit, "Trim")
draft = Units.Quantity(Locale.fromString(
form.draft.text())).getValueAs('m').Value
trim = Units.Quantity(Locale.fromString(
form.trim.text())).getValueAs('deg').Value
props = self.ship.PropertiesList
try:
props.index("AreaCurveDraft")
except ValueError:
try:
tooltip = str(QtGui.QApplication.translate(
"ship_areas",
"Areas curve tool draft selected [m]",
None,
QtGui.QApplication.UnicodeUTF8))
except:
tooltip = "Areas curve tool draft selected [m]"
self.ship.addProperty("App::PropertyLength",
"AreaCurveDraft",
"Ship",
tooltip)
self.ship.AreaCurveDraft = '{} m'.format(draft)
try:
props.index("AreaCurveTrim")
except ValueError:
try:
tooltip = str(QtGui.QApplication.translate(
"ship_areas",
"Areas curve tool trim selected [deg]",
None,
QtGui.QApplication.UnicodeUTF8))
except:
tooltip = "Areas curve tool trim selected [deg]"
self.ship.addProperty("App::PropertyAngle",
"AreaCurveTrim",
"Ship",
tooltip)
self.ship.AreaCurveTrim = '{} deg'.format(trim)
def createTask():
panel = TaskPanel()
Gui.Control.showDialog(panel)
if panel.setupUi():
Gui.Control.closeDialog(panel)
return None
return panel

View File

@ -0,0 +1,29 @@
#***************************************************************************
#* *
#* 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 *
#* *
#***************************************************************************
import TaskPanel
def load():
""" Loads the tool """
TaskPanel.createTask()