Updated the hydrostatics tool

This commit is contained in:
Jose Luis Cercos-Pita 2014-05-03 13:28:57 +02:00 committed by wmayer
parent 1801857108
commit e7c3e264ff
6 changed files with 1214 additions and 1175 deletions

View File

@ -45,20 +45,16 @@ class ShipWorkbench(Workbench):
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintMessage(msg + '\n')
# ToolBar
"""
shiplist = ["Ship_LoadExample",
"Ship_CreateShip",
"Ship_OutlineDraw",
"Ship_AreasCurve",
"Ship_Hydrostatics"]
"""
weightslist = ["Ship_Weights",
"Ship_CreateTank",
"Ship_GZ"]
"""
shiplist = ["Ship_LoadExample",
"Ship_CreateShip",
"Ship_OutlineDraw",
"Ship_AreasCurve"]
weightslist = []
self.appendToolbar(
str(QtCore.QT_TRANSLATE_NOOP("Ship", "Ship design")),
@ -73,46 +69,4 @@ class ShipWorkbench(Workbench):
str(QtCore.QT_TRANSLATE_NOOP("Ship", "Weights")),
weightslist)
# Simulation tools will be added only if pyOpenCL & numpy are present
# Simulations are in development, so keep it disabled except for
# development purposes
hasSim = False
if hasSim:
hasOpenCL = True
hasNumpy = True
try:
import pyopencl
except ImportError:
hasOpenCL = False
msg = QtGui.QApplication.translate(
"ship_console",
"pyOpenCL not installed, simulation tools will disabled"
" therefore",
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintMessage(msg + '\n')
try:
import numpy
except ImportError:
hasNumpy = False
msg = QtGui.QApplication.translate(
"ship_console",
"numpy not installed, simulation tools will disabled"
" therefore",
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintMessage(msg + '\n')
if hasOpenCL and hasNumpy:
simlist = ["Ship_CreateSim",
"Ship_RunSim",
"Ship_StopSim",
"Ship_TrackSim"]
self.appendToolbar(
str(QtCore.QT_TRANSLATE_NOOP("Ship", "Simulation")),
simlist)
self.appendMenu(
str(QtCore.QT_TRANSLATE_NOOP("Ship", "Simulation")),
simlist)
Gui.addWorkbench(ShipWorkbench())

View File

@ -114,7 +114,7 @@ class Hydrostatics:
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'Ship_Hydrostatics',
'Plot the ship hydrostatics')
return {'Pixmap': 'HydrostaticsIco',
return {'Pixmap': 'Ship_Hydrostatics',
'MenuText': MenuText,
'ToolTip': ToolTip}
@ -170,74 +170,6 @@ class GZ:
'ToolTip': ToolTip}
class CreateSim:
def Activated(self):
import simCreate
simCreate.load()
def GetResources(self):
MenuText = QtCore.QT_TRANSLATE_NOOP(
'Ship_CreateSim',
'Create a new simulation')
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'Ship_CreateSim',
'Create a new simulation in order to process later')
return {'Pixmap': 'SimCreateIco',
'MenuText': MenuText,
'ToolTip': ToolTip}
class RunSim:
def Activated(self):
import simRun
simRun.load()
def GetResources(self):
MenuText = QtCore.QT_TRANSLATE_NOOP(
'Ship_RunSim',
'Run a simulation')
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'Ship_RunSim',
'Run a simulation')
return {'Pixmap': 'SimRunIco',
'MenuText': MenuText,
'ToolTip': ToolTip}
class StopSim:
def Activated(self):
import simRun
simRun.stop()
def GetResources(self):
MenuText = QtCore.QT_TRANSLATE_NOOP(
'Ship_StopSim',
'Stop active simulation')
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'Ship_StopSim',
'Stop active simulation')
return {'Pixmap': 'SimStopIco',
'MenuText': MenuText,
'ToolTip': ToolTip}
class TrackSim:
def Activated(self):
import simPost
simPost.load()
def GetResources(self):
MenuText = QtCore.QT_TRANSLATE_NOOP(
'Ship_TrackSim',
'Track simulation')
ToolTip = QtCore.QT_TRANSLATE_NOOP(
'Ship_TrackSim',
'Track simulation')
return {'Pixmap': 'SimPostIco',
'MenuText': MenuText,
'ToolTip': ToolTip}
FreeCADGui.addCommand('Ship_LoadExample', LoadExample())
FreeCADGui.addCommand('Ship_CreateShip', CreateShip())
FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw())
@ -246,4 +178,3 @@ FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics())
FreeCADGui.addCommand('Ship_Weights', SetWeights())
FreeCADGui.addCommand('Ship_CreateTank', CreateTank())
FreeCADGui.addCommand('Ship_GZ', GZ())
FreeCADGui.addCommand('Ship_CreateSim', CreateSim())

View File

@ -1,371 +1,394 @@
#***************************************************************************
#* *
#* Copyright (c) 2011, 2012 *
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
#* *
#* *
#* 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 *
#* 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 *
#* *
#* USA *
#* *
#***************************************************************************
import os
import math
# Qt library
from PyQt4 import QtGui,QtCore
# FreeCAD modules
import FreeCAD,FreeCADGui
# FreeCADShip modules
from PySide import QtGui, QtCore
import FreeCAD
import FreeCADGui
from shipUtils import Paths
header = """ #################################################################
##### #### ### #### ##### # # ### ####
# # # # # # # # # # # #
# ## #### #### # # # # # # # # # # #
#### # # # # # # # ##### # # ## ## ##### # ####
# # #### #### # # # # # # # # # #
# # # # # # # # # # # # # #
# # #### #### ### # # #### ##### # # ### #
##### #### ### #### ##### # # ### ####
# # # # # # # # # # # #
# ## #### #### # # # # # # # # # # #
#### # # # # # # # ##### # # ## ## ##### # ####
# # #### #### # # # # # # # # # #
# # # # # # # # # # # # # #
# # #### #### ### # # #### ##### # # ### #
#################################################################
"""
class Plot(object):
def __init__(self, ship, trim, points):
""" Constructor. performs plot and show it (Using pyxplot).
@param ship Selected ship instance
@param trim Trim in degrees.
@param points List of computed hydrostatics.
"""
self.points = points[:]
# Try to plot
self.plotVolume()
self.plotStability()
self.plotCoeffs()
# Save data
if self.createDirectory():
return
if self.saveData(ship, trim):
return
def __init__(self, ship, trim, points):
""" Constructor. performs plot and show it (Using pyxplot).
@param ship Selected ship instance
@param trim Trim in degrees.
@param points List of computed hydrostatics.
"""
self.points = points[:]
# Try to plot
self.plotVolume()
self.plotStability()
self.plotCoeffs()
# Save data
if self.createDirectory():
return
if self.saveData(ship, trim):
return
def plotVolume(self):
""" Perform volumetric hydrostatics.
@return True if error happens.
"""
# Create plot
try:
import Plot
plt = Plot.figure('Volume')
except ImportError:
msg = QtGui.QApplication.translate("ship_console", "Plot module is disabled, can't perform plot",
None,QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintWarning(msg + '\n')
return True
# Generate sets of axes
Plot.grid(True)
for i in range(0,3):
ax = Plot.addNewAxes()
# Y axes can be moved to right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0,0.0,0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axes moved down with an offset
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward',(i+1)*35))
Plot.grid(True)
# Setup data
disp = []
draft = []
warea = []
t1cm = []
xcb = []
for i in range(0,len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
warea.append(self.points[i].wet)
t1cm.append(self.points[i].mom)
xcb.append(self.points[i].xcb)
# Set plot size
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
# Plot curves
plt.axes = axes[0]
serie = Plot.plot(draft,disp,r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,0.0))
Plot.xlabel(r'$T \; \mathrm{m}$')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(warea,disp,r'Wetted area')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0,0.0,0.0))
Plot.xlabel(r'$Wetted \; area \; \mathrm{m}^2$')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(t1cm,disp,r'Moment to trim 1cm')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,1.0))
Plot.xlabel(r'$Moment \; to \; trim \; 1 \mathrm{cm} \; \mathrm{tons} \; \times \; \mathrm{m}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(xcb,disp,r'$XCB$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2,0.8,0.2))
Plot.xlabel(r'$XCB \; \mathrm{m}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
# Show legend
Plot.legend(True)
# End
plt.update()
return False
def plotVolume(self):
""" Perform volumetric hydrostatics.
@return True if error happens.
"""
try:
import Plot
plt = Plot.figure('Volume')
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
def plotStability(self):
""" Perform stability hydrostatics.
@return True if error happens.
"""
# Create plot
try:
import Plot
plt = Plot.figure('Stability')
except ImportError:
return True
# Generate sets of axes
Plot.grid(True)
for i in range(0,3):
ax = Plot.addNewAxes()
# Y axes can be moved to right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0,0.0,0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axes moved down with an offset
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward',(i+1)*35))
Plot.grid(True)
# Setup data
disp = []
draft = []
farea = []
kbt = []
bmt = []
for i in range(0,len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
farea.append(self.points[i].farea)
kbt.append(self.points[i].KBt)
bmt.append(self.points[i].BMt)
# Set plot size
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
# Plot curves
plt.axes = axes[0]
serie = Plot.plot(draft,disp,r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,0.0))
Plot.xlabel(r'$T \; \mathrm{m}$')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(farea,disp,r'Floating area')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0,0.0,0.0))
Plot.xlabel(r'$Floating \; area \; \mathrm{m}^2$')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(kbt,disp,r'$KB_T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,1.0))
Plot.xlabel(r'$KB_T \; \mathrm{m}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(bmt,disp,r'$BM_T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2,0.8,0.2))
Plot.xlabel(r'$BM_T \; \mathrm{m}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
# Show legend
Plot.legend(True)
# End
plt.update()
return False
# Generate the set of axes
Plot.grid(True)
for i in range(0, 3):
ax = Plot.addNewAxes()
# Y axis can be placed at right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0, 0.0, 0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axis can be placed at bottom
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward', (i + 1) * 35))
Plot.grid(True)
def plotCoeffs(self):
""" Perform stability hydrostatics.
@return True if error happens.
"""
# Create plot
try:
import Plot
plt = Plot.figure('Coefficients')
except ImportError:
return True
# Generate sets of axes
Plot.grid(True)
for i in range(0,3):
ax = Plot.addNewAxes()
# Y axes can be moved to right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0,0.0,0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axes moved down with an offset
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward',(i+1)*40))
Plot.grid(True)
# Setup data
disp = []
draft = []
cb = []
cf = []
cm = []
for i in range(0,len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
cb.append(self.points[i].Cb)
cf.append(self.points[i].Cf)
cm.append(self.points[i].Cm)
# Set plot size
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
# Plot curves
plt.axes = axes[0]
serie = Plot.plot(draft,disp,r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,0.0))
Plot.xlabel(r'$T \; \mathrm{m}$')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(cb,disp,r'$Cb$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0,0.0,0.0))
Plot.xlabel(r'$Cb$ (Block coefficient)')
Plot.ylabel(r'$\bigtriangleup \; \mathrm{tons}$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(cf,disp,r'$Cf$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0,0.0,1.0))
Plot.xlabel(r'$Cf$ (floating area coefficient)')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(cm,disp,r'$Cm$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2,0.8,0.2))
Plot.xlabel(r'$Cm$ (Main section coefficient)')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
# Show legend
Plot.legend(True)
# End
plt.update()
return False
disp = []
draft = []
warea = []
t1cm = []
xcb = []
for i in range(len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
warea.append(self.points[i].wet)
t1cm.append(self.points[i].mom)
xcb.append(self.points[i].xcb)
def createDirectory(self):
""" Create needed folder to write data and scripts.
@return True if error happens.
"""
self.path = FreeCAD.ConfigGet("UserAppData") + "ShipOutput/"
if not os.path.exists(self.path):
os.makedirs(self.path)
if not os.path.exists(self.path):
msg = QtGui.QApplication.translate("ship_console", "Can't create folder",
None,QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintError(msg + ':\n\t' + "\'"+ self.path + "\'\n")
return False
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
def saveData(self, ship, trim):
""" Write data file.
@param ship Selected ship instance
@param trim Trim in degrees.
@return True if error happens.
"""
# Open the file
filename = self.path + 'hydrostatics.dat'
try:
Output = open(filename, "w")
except IOError:
msg = QtGui.QApplication.translate("ship_console", "Can't write to file",
None,QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintError(msg + ':\n\t' + "\'"+ filename + "\'\n")
return True
# Print header
Output.write(header)
Output.write(" #\n")
Output.write(" # File automatically exported by FreeCAD-Ship\n")
Output.write(" # This file contains transversal areas data, filled with following columns:\n")
Output.write(" # 1: Ship displacement [ton]\n")
Output.write(" # 2: Draft [m]\n")
Output.write(" # 3: Wetted surface [m2]\n")
Output.write(" # 4: 1cm triming ship moment [ton m]\n")
Output.write(" # 5: Bouyance center x coordinate\n")
Output.write(" # 6: Floating area\n")
Output.write(" # 7: KBt\n")
Output.write(" # 8: BMt\n")
Output.write(" # 9: Cb (block coefficient)\n")
Output.write(" # 10: Cf (Floating coefficient)\n")
Output.write(" # 11: Cm (Main frame coefficient)\n")
Output.write(" #\n")
Output.write(" #################################################################\n")
# Print data
for i in range(0,len(self.points)):
point = self.points[i]
string = "%f %f %f %f %f %f %f %f %f %f %f\n" % (point.disp, point.draft, point.wet, point.mom, point.xcb, point.farea, point.KBt, point.BMt, point.Cb, point.Cf, point.Cm)
Output.write(string)
# Close file
Output.close()
self.dataFile = filename
msg = QtGui.QApplication.translate("ship_console", "Data saved",
None,QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintMessage(msg + ':\n\t' + "\'"+ self.dataFile + "\'\n")
return False
plt.axes = axes[0]
serie = Plot.plot(draft, disp, r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 0.0))
Plot.xlabel(r'$T \; \left[ \mathrm{m} \right]$')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(warea, disp, r'Wetted area')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0, 0.0, 0.0))
Plot.xlabel(r'$Wetted \; area \; \left[ \mathrm{m}^2 \right]$')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(t1cm, disp, r'Moment to trim 1cm')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 1.0))
Plot.xlabel(r'$Moment \; to \; trim \; 1 \mathrm{cm} \; \left['
r' \mathrm{tons} \; \times \; \mathrm{m} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(xcb, disp, r'$XCB$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2, 0.8, 0.2))
Plot.xlabel(r'$XCB \; \left[ \mathrm{m} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
Plot.legend(True)
plt.update()
return False
def plotStability(self):
""" Perform stability hydrostatics.
@return True if error happens.
"""
try:
import Plot
plt = Plot.figure('Stability')
except ImportError:
return True
# Generate the sets of axes
Plot.grid(True)
for i in range(0, 3):
ax = Plot.addNewAxes()
# Y axis can be placed at right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0, 0.0, 0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axis can be placed at bottom
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward', (i + 1) * 35))
Plot.grid(True)
disp = []
draft = []
farea = []
kbt = []
bmt = []
for i in range(len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
farea.append(self.points[i].farea)
kbt.append(self.points[i].KBt)
bmt.append(self.points[i].BMt)
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
plt.axes = axes[0]
serie = Plot.plot(draft, disp, r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 0.0))
Plot.xlabel(r'$T \; \left[ \mathrm{m}$ \right]')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(farea, disp, r'Floating area')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0, 0.0, 0.0))
Plot.xlabel(r'$Floating \; area \; \left[ \mathrm{m}^2 \right]$')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(kbt, disp, r'$KB_T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 1.0))
Plot.xlabel(r'$KB_T \; \left[ \mathrm{m} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(bmt, disp, r'$BM_T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2, 0.8, 0.2))
Plot.xlabel(r'$BM_T \; \left[ \mathrm{m} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
Plot.legend(True)
plt.update()
return False
def plotCoeffs(self):
""" Perform stability hydrostatics.
@return True if error happens.
"""
# Create plot
try:
import Plot
plt = Plot.figure('Coefficients')
except ImportError:
return True
# Generate the set of axes
Plot.grid(True)
for i in range(0, 3):
ax = Plot.addNewAxes()
# Y axis can be placed at right
ax.yaxis.tick_right()
ax.spines['right'].set_color((0.0, 0.0, 0.0))
ax.spines['left'].set_color('none')
ax.yaxis.set_ticks_position('right')
ax.yaxis.set_label_position('right')
# And X axis can be placed at bottom
for loc, spine in ax.spines.iteritems():
if loc in ['bottom', 'top']:
spine.set_position(('outward', (i + 1) * 35))
Plot.grid(True)
disp = []
draft = []
cb = []
cf = []
cm = []
for i in range(len(self.points)):
disp.append(self.points[i].disp)
draft.append(self.points[i].draft)
cb.append(self.points[i].Cb)
cf.append(self.points[i].Cf)
cm.append(self.points[i].Cm)
axes = Plot.axesList()
for ax in axes:
ax.set_position([0.1, 0.2, 0.8, 0.75])
plt.axes = axes[0]
serie = Plot.plot(draft, disp, r'$T$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 0.0))
Plot.xlabel(r'$T \; \left[ \mathrm{m} \right]$')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[1]
serie = Plot.plot(cb, disp, r'$Cb$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((1.0, 0.0, 0.0))
Plot.xlabel(r'$Cb$ (Block coefficient)')
Plot.ylabel(r'$\bigtriangleup \; \left[ \mathrm{tons} \right]$')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[2]
serie = Plot.plot(cf, disp, r'$Cf$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.0, 0.0, 1.0))
Plot.xlabel(r'$Cf$ (floating area coefficient)')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
plt.axes = axes[3]
serie = Plot.plot(cm, disp, r'$Cm$')
serie.line.set_linestyle('-')
serie.line.set_linewidth(2.0)
serie.line.set_color((0.2, 0.8, 0.2))
Plot.xlabel(r'$Cm$ (Main section coefficient)')
plt.axes.xaxis.label.set_fontsize(15)
plt.axes.yaxis.label.set_fontsize(15)
Plot.legend(True)
plt.update()
return False
def createDirectory(self):
""" Create needed folder to write data and scripts.
@return True if error happens.
"""
self.path = FreeCAD.ConfigGet("UserAppData") + "ShipOutput/"
if not os.path.exists(self.path):
os.makedirs(self.path)
if not os.path.exists(self.path):
msg = QtGui.QApplication.translate(
"ship_console",
"Failure creating the folder".format(self.path),
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintError(msg + ":\n\t'" + self.path + "'\n")
return False
def saveData(self, ship, trim):
""" Write data file.
@param ship Selected ship instance
@param trim Trim in degrees.
@return True if error happens.
"""
# Open the file
filename = self.path + 'hydrostatics.dat'
try:
Output = open(filename, "w")
except IOError:
msg = QtGui.QApplication.translate(
"ship_console",
"Failure writing the file",
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintError(msg + ":\n\t'" + filename + "'\n")
return True
Output.write(header)
Output.write(" #\n")
Output.write(" # File automatically exported by FreeCAD-Ship\n")
Output.write(" # This file contains transversal areas data, filled"
" with following columns:\n")
Output.write(" # 1: Ship displacement [ton]\n")
Output.write(" # 2: Draft [m]\n")
Output.write(" # 3: Wetted surface [m2]\n")
Output.write(" # 4: 1cm triming ship moment [ton m]\n")
Output.write(" # 5: Bouyance center x coordinate\n")
Output.write(" # 6: Floating area\n")
Output.write(" # 7: KBt\n")
Output.write(" # 8: BMt\n")
Output.write(" # 9: Cb (block coefficient)\n")
Output.write(" # 10: Cf (Floating coefficient)\n")
Output.write(" # 11: Cm (Main frame coefficient)\n")
Output.write(" #\n")
Output.write(" ######################################################"
"###########\n")
for i in range(len(self.points)):
point = self.points[i]
string = "{} {} {} {} {} {} {} {} {} {} {}\n".format(
point.disp,
point.draft,
point.wet,
point.mom,
point.xcb,
point.farea,
point.KBt,
point.BMt,
point.Cb,
point.Cf,
point.Cm)
Output.write(string)
Output.close()
self.dataFile = filename
msg = QtGui.QApplication.translate(
"ship_console",
"Data saved",
None,
QtGui.QApplication.UnicodeUTF8)
FreeCAD.Console.PrintMessage(msg + ":\n\t'" + self.dataFile + "'\n")
return False

View File

@ -21,350 +21,465 @@
#* *
#***************************************************************************
import math
import FreeCAD as App
import FreeCADGui as Gui
from FreeCAD import Base, Vector
import Part
from PyQt4 import QtGui,QtCore
import Units
from PySide import QtGui, QtCore
import PlotAux
import Instance
from shipUtils import Paths
import Tools
class TaskPanel:
def __init__(self):
self.ui = Paths.modulePath() + "/shipHydrostatics/TaskPanel.ui"
self.ship = None
self.running = False
def __init__(self):
self.ui = Paths.modulePath() + "/shipHydrostatics/TaskPanel.ui"
self.ship = None
self.running = False
def accept(self):
if not self.ship:
return False
if self.running:
return
self.save()
draft = self.form.minDraft.value()
drafts = [draft]
dDraft = (self.form.maxDraft.value() - self.form.minDraft.value())/(self.form.nDraft.value()-1)
for i in range(1,self.form.nDraft.value()):
draft = draft + dDraft
drafts.append(draft)
# Compute data
# Get external faces
self.loop=QtCore.QEventLoop()
self.timer=QtCore.QTimer()
self.timer.setSingleShot(True)
QtCore.QObject.connect(self.timer,QtCore.SIGNAL("timeout()"),self.loop,QtCore.SLOT("quit()"))
self.running = True
faces = self.externalFaces(self.ship.Shape)
if not self.running:
return False
if len(faces) == 0:
msg = QtGui.QApplication.translate("ship_console", "Can't detect external faces from ship object",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
return False
faces = Part.makeShell(faces)
# Get hydrostatics
msg = QtGui.QApplication.translate("ship_console", "Computing hydrostatics",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintMessage(msg + '...\n')
points = []
for i in range(0,len(drafts)):
App.Console.PrintMessage("\t%d / %d\n" % (i+1, len(drafts)))
draft = drafts[i]
point = Tools.Point(self.ship,faces,draft,self.form.trim.value())
points.append(point)
self.timer.start(0.0)
self.loop.exec_()
if(not self.running):
break
PlotAux.Plot(self.ship, self.form.trim.value(), points)
return True
def accept(self):
if not self.ship:
return False
if self.running:
return
self.save()
def reject(self):
if not self.ship:
return False
if self.running:
self.running = False
return
return True
min_draft = self.form.minDraft.value()
max_draft = self.form.maxDraft.value()
n_draft = self.form.nDraft.value()
def clicked(self, index):
pass
draft = min_draft
drafts = [draft]
dDraft = (max_draft - min_draft) / (n_draft - 1)
for i in range(1, n_draft):
draft = draft + dDraft
drafts.append(draft)
def open(self):
pass
# Compute data
# Get external faces
self.loop = QtCore.QEventLoop()
self.timer = QtCore.QTimer()
self.timer.setSingleShot(True)
QtCore.QObject.connect(self.timer,
QtCore.SIGNAL("timeout()"),
self.loop,
QtCore.SLOT("quit()"))
self.running = True
faces = self.externalFaces(self.ship.Shape)
if not self.running:
return False
if len(faces) == 0:
msg = QtGui.QApplication.translate(
"ship_console",
"Failure detecting external faces from the ship object",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
return False
faces = Part.makeShell(faces)
# Get the hydrostatics
msg = QtGui.QApplication.translate(
"ship_console",
"Computing hydrostatics",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintMessage(msg + '...\n')
points = []
for i in range(len(drafts)):
App.Console.PrintMessage("\t{} / {}\n".format(i + 1, len(drafts)))
draft = drafts[i]
point = Tools.Point(self.ship,
faces,
draft,
self.form.trim.value())
points.append(point)
self.timer.start(0.0)
self.loop.exec_()
if(not self.running):
break
PlotAux.Plot(self.ship, self.form.trim.value(), points)
return True
def needsFullSpace(self):
return True
def reject(self):
if not self.ship:
return False
if self.running:
self.running = False
return
return True
def isAllowedAlterSelection(self):
return False
def clicked(self, index):
pass
def isAllowedAlterView(self):
return True
def open(self):
pass
def isAllowedAlterDocument(self):
return False
def needsFullSpace(self):
return True
def helpRequested(self):
pass
def isAllowedAlterSelection(self):
return False
def setupUi(self):
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.trim = form.findChild(QtGui.QDoubleSpinBox, "Trim")
form.minDraft = form.findChild(QtGui.QDoubleSpinBox, "MinDraft")
form.maxDraft = form.findChild(QtGui.QDoubleSpinBox, "MaxDraft")
form.nDraft = form.findChild(QtGui.QSpinBox, "NDraft")
self.form = form
# Initial values
if self.initValues():
return True
self.retranslateUi()
# Connect Signals and Slots
QtCore.QObject.connect(form.trim, QtCore.SIGNAL("valueChanged(double)"), self.onData)
QtCore.QObject.connect(form.minDraft, QtCore.SIGNAL("valueChanged(double)"), self.onData)
QtCore.QObject.connect(form.maxDraft, QtCore.SIGNAL("valueChanged(double)"), self.onData)
def isAllowedAlterView(self):
return True
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 isAllowedAlterDocument(self):
return False
def initValues(self):
""" Set initial values for fields
"""
# Get objects
selObjs = Gui.Selection.getSelection()
if not selObjs:
msg = QtGui.QApplication.translate("ship_console", "Ship instance must be selected (no object selected)",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
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 = QtGui.QApplication.translate("ship_console", "More than one ship selected (extra ships will be neglected)",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintWarning(msg + '\n')
break
self.ship = obj
# Test if any valid ship was selected
if not self.ship:
msg = QtGui.QApplication.translate("ship_console",
"Ship instance must be selected (no valid ship found at selected objects)",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + '\n')
return True
# Get bounds
bbox = self.ship.Shape.BoundBox
# Set trim
flag = True
try:
props.index("HydrostaticsTrim")
except ValueError:
flag = False
if flag:
self.form.trim.setValue(self.ship.HydrostaticsTrim)
# Set drafts
self.form.maxDraft.setValue(1.1*self.ship.Draft)
self.form.minDraft.setValue(0.9*self.ship.Draft)
# Try to use saved values
props = self.ship.PropertiesList
flag = True
try:
props.index("HydrostaticsMinDraft")
except ValueError:
flag = False
if flag:
self.form.minDraft.setValue(self.ship.HydrostaticsMinDraft)
flag = True
try:
props.index("HydrostaticsMaxDraft")
except ValueError:
flag = False
if flag:
self.form.maxDraft.setValue(self.ship.HydrostaticsMaxDraft)
self.form.maxDraft.setMaximum(bbox.ZMax)
self.form.minDraft.setMinimum(bbox.ZMin)
self.form.maxDraft.setMinimum(self.form.minDraft.value())
self.form.minDraft.setMaximum(self.form.maxDraft.value())
flag = True
try:
props.index("HydrostaticsNDraft")
except ValueError:
flag = False
if flag:
self.form.nDraft.setValue(self.ship.HydrostaticsNDraft)
# Update GUI
return False
def helpRequested(self):
pass
def retranslateUi(self):
""" Set user interface locale strings.
"""
self.form.setWindowTitle(QtGui.QApplication.translate("ship_hydrostatic","Plot hydrostatics",
None,QtGui.QApplication.UnicodeUTF8))
self.form.findChild(QtGui.QLabel, "TrimLabel").setText(QtGui.QApplication.translate("ship_hydrostatic","Trim",
None,QtGui.QApplication.UnicodeUTF8))
self.form.findChild(QtGui.QLabel, "MinDraftLabel").setText(QtGui.QApplication.translate("ship_hydrostatic","Minimum draft",
None,QtGui.QApplication.UnicodeUTF8))
self.form.findChild(QtGui.QLabel, "MaxDraftLabel").setText(QtGui.QApplication.translate("ship_hydrostatic","Maximum draft",
None,QtGui.QApplication.UnicodeUTF8))
self.form.findChild(QtGui.QLabel, "NDraftLabel").setText(QtGui.QApplication.translate("ship_hydrostatic","Number of points",
None,QtGui.QApplication.UnicodeUTF8))
def setupUi(self):
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.trim = self.widget(QtGui.QDoubleSpinBox, "Trim")
form.minDraft = self.widget(QtGui.QDoubleSpinBox, "MinDraft")
form.maxDraft = self.widget(QtGui.QDoubleSpinBox, "MaxDraft")
form.nDraft = self.widget(QtGui.QSpinBox, "NDraft")
self.form = form
# Initial values
if self.initValues():
return True
self.retranslateUi()
# Connect Signals and Slots
QtCore.QObject.connect(form.trim,
QtCore.SIGNAL("valueChanged(double)"),
self.onData)
QtCore.QObject.connect(form.minDraft,
QtCore.SIGNAL("valueChanged(double)"),
self.onData)
QtCore.QObject.connect(form.maxDraft,
QtCore.SIGNAL("valueChanged(double)"),
self.onData)
def onData(self, value):
""" Method called when input data is changed.
@param value Changed value.
"""
if not self.ship:
return
self.form.maxDraft.setMinimum(self.form.minDraft.value())
self.form.minDraft.setMaximum(self.form.maxDraft.value())
def getMainWindow(self):
toplevel = QtGui.qApp.topLevelWidgets()
for i in toplevel:
if i.metaObject().className() == "Gui::MainWindow":
return i
raise Exception("No main window found")
def save(self):
""" Saves data into ship instance.
"""
props = self.ship.PropertiesList
try:
props.index("HydrostaticsTrim")
except ValueError:
tooltip = str(QtGui.QApplication.translate("ship_hydrostatic","Hydrostatics tool trim selected",
None,QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyFloat","HydrostaticsTrim","Ship", tooltip)
self.ship.HydrostaticsTrim = self.form.trim.value()
try:
props.index("HydrostaticsMinDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate("ship_hydrostatic","Hydrostatics tool minimum draft selected [m]",
None,QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyFloat","HydrostaticsMinDraft","Ship", tooltip)
self.ship.HydrostaticsMinDraft = self.form.minDraft.value()
try:
props.index("HydrostaticsMaxDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate("ship_hydrostatic","Hydrostatics tool maximum draft selected [m]",
None,QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyFloat","HydrostaticsMaxDraft","Ship", tooltip)
self.ship.HydrostaticsMaxDraft = self.form.maxDraft.value()
try:
props.index("HydrostaticsNDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate("ship_hydrostatic","Hydrostatics tool number of points selected",
None,QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyInteger","HydrostaticsNDraft","Ship", tooltip)
self.ship.HydrostaticsNDraft = self.form.nDraft.value()
def widget(self, class_id, name):
"""Return the selected widget.
def lineFaceSection(self,line,surface):
""" Returns the point of section of a line with a face
@param line Line object, that can be a curve.
@param surface Surface object (must be a Part::Shape)
@return Section points array, [] if line don't cut surface
"""
# Get initial data
result = []
vertexes = line.Vertexes
nVertex = len(vertexes)
# Perform the cut
section = line.cut(surface)
# Filter all old points
points = section.Vertexes
return points
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
"""
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.trim = self.widget(QtGui.QDoubleSpinBox, "Trim")
form.minDraft = self.widget(QtGui.QDoubleSpinBox, "MinDraft")
form.maxDraft = self.widget(QtGui.QDoubleSpinBox, "MaxDraft")
form.nDraft = self.widget(QtGui.QSpinBox, "NDraft")
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(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
bbox = self.ship.Shape.BoundBox
props = self.ship.PropertiesList
try:
props.index("HydrostaticsTrim")
form.trim.setValue(
self.ship.HydrostaticsTrim.getValueAs('deg').Value)
except ValueError:
pass
try:
props.index("HydrostaticsMinDraft")
form.minDraft.setValue(
self.ship.HydrostaticsMinDraft.getValueAs('m').Value)
except ValueError:
App.Console.PrintWarning(0.9 * self.ship.Draft.getValueAs('m').Value)
App.Console.PrintWarning('\n')
form.minDraft.setValue(
0.9 * self.ship.Draft.getValueAs('m').Value)
try:
props.index("HydrostaticsMaxDraft")
form.maxDraft.setValue(
self.ship.HydrostaticsMaxDraft.getValueAs('m').Value)
except ValueError:
App.Console.PrintWarning(1.1 * self.ship.Draft.getValueAs('m').Value)
App.Console.PrintWarning('\n')
form.maxDraft.setValue(
1.1 * self.ship.Draft.getValueAs('m').Value)
App.Console.PrintWarning(form.minDraft.value())
App.Console.PrintWarning('\n')
form.maxDraft.setMaximum(bbox.ZMax / Units.Metre.Value)
form.minDraft.setMinimum(bbox.ZMin / Units.Metre.Value)
form.maxDraft.setMinimum(form.minDraft.value())
form.minDraft.setMaximum(form.maxDraft.value())
App.Console.PrintWarning(form.minDraft.value())
App.Console.PrintWarning('\n')
try:
props.index("HydrostaticsNDraft")
form.nDraft.setValue(self.ship.HydrostaticsNDraft.Value)
except ValueError:
pass
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_hydrostatic",
"Plot hydrostatics",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "TrimLabel").setText(
QtGui.QApplication.translate(
"ship_hydrostatic",
"Trim",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "MinDraftLabel").setText(
QtGui.QApplication.translate(
"ship_hydrostatic",
"Minimum draft",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "MaxDraftLabel").setText(
QtGui.QApplication.translate(
"ship_hydrostatic",
"Maximum draft",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QLabel, "NDraftLabel").setText(
QtGui.QApplication.translate(
"ship_hydrostatic",
"Number of points",
None,
QtGui.QApplication.UnicodeUTF8))
def onData(self, value):
""" Method called when input data is changed.
@param value Changed value.
"""
if not self.ship:
return
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.minDraft = self.widget(QtGui.QDoubleSpinBox, "MinDraft")
form.maxDraft = self.widget(QtGui.QDoubleSpinBox, "MaxDraft")
form.maxDraft.setMinimum(form.minDraft.value())
form.minDraft.setMaximum(form.maxDraft.value())
def save(self):
""" Saves data into ship instance.
"""
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.trim = self.widget(QtGui.QDoubleSpinBox, "Trim")
form.minDraft = self.widget(QtGui.QDoubleSpinBox, "MinDraft")
form.maxDraft = self.widget(QtGui.QDoubleSpinBox, "MaxDraft")
form.nDraft = self.widget(QtGui.QSpinBox, "NDraft")
props = self.ship.PropertiesList
try:
props.index("HydrostaticsTrim")
except ValueError:
tooltip = str(QtGui.QApplication.translate(
"ship_hydrostatic",
"Hydrostatics tool trim selected",
None,
QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyAngle",
"HydrostaticsTrim",
"Ship",
tooltip)
self.ship.HydrostaticsTrim = '{} deg'.format(form.trim.value())
try:
props.index("HydrostaticsMinDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate(
"ship_hydrostatic",
"Hydrostatics tool minimum draft selected [m]",
None,
QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyLength",
"HydrostaticsMinDraft",
"Ship",
tooltip)
self.ship.HydrostaticsMinDraft = '{} m'.format(form.minDraft.value())
try:
props.index("HydrostaticsMaxDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate(
"ship_hydrostatic",
"Hydrostatics tool maximum draft selected [m]",
None,
QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyLength",
"HydrostaticsMaxDraft",
"Ship",
tooltip)
self.ship.HydrostaticsMaxDraft = '{} m'.format(form.maxDraft.value())
try:
props.index("HydrostaticsNDraft")
except ValueError:
tooltip = str(QtGui.QApplication.translate(
"ship_hydrostatic",
"Hydrostatics tool number of points selected",
None,
QtGui.QApplication.UnicodeUTF8))
self.ship.addProperty("App::PropertyInteger",
"HydrostaticsNDraft",
"Ship",
tooltip)
self.ship.HydrostaticsNDraft = form.nDraft.value()
def lineFaceSection(self, line, surface):
""" Returns the point of section of a line with a face
@param line Line object, that can be a curve.
@param surface Surface object (must be a Part::Shape)
@return Section points array, [] if line don't cut surface
"""
result = []
vertexes = line.Vertexes
nVertex = len(vertexes)
section = line.cut(surface)
points = section.Vertexes
return points
def externalFaces(self, shape):
""" Returns detected external faces.
@param shape Shape where external faces wanted.
@return List of external faces detected.
"""
result = []
faces = shape.Faces
bbox = shape.BoundBox
L = bbox.XMax - bbox.XMin
B = bbox.YMax - bbox.YMin
T = bbox.ZMax - bbox.ZMin
dist = math.sqrt(L*L + B*B + T*T)
msg = QtGui.QApplication.translate(
"ship_console",
"Computing external faces",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintMessage(msg + '...\n')
# Valid/unvalid faces detection loop
for i in range(len(faces)):
App.Console.PrintMessage("\t{} / {}\n".format(i + 1, len(faces)))
f = faces[i]
# Create a line normal to surface at middle point
u = 0.0
v = 0.0
try:
surf = f.Surface
u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1])
v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1])
except:
cog = f.CenterOfMass
[u, v] = f.Surface.parameter(cog)
p0 = f.valueAt(u, v)
try:
n = f.normalAt(u, v).normalize()
except:
continue
p1 = p0 + n.multiply(1.5 * dist)
line = Part.makeLine(p0, p1)
# Look for faces in front of this
nPoints = 0
for j in range(len(faces)):
f2 = faces[j]
section = self.lineFaceSection(line, f2)
if len(section) <= 2:
continue
# Add points discarding start and end
nPoints = nPoints + len(section) - 2
# In order to avoid special directions we can modify line
# normal a little bit.
angle = 5
line.rotate(p0, Vector(1, 0, 0), angle)
line.rotate(p0, Vector(0, 1, 0), angle)
line.rotate(p0, Vector(0, 0, 1), angle)
nPoints2 = 0
for j in range(len(faces)):
if i == j:
continue
f2 = faces[j]
section = self.lineFaceSection(line, f2)
if len(section) <= 2:
continue
# Add points discarding start and end
nPoints2 = nPoints + len(section) - 2
# If the number of intersection points is pair, is a
# external face. So if we found an odd points intersection,
# face must be discarded.
if (nPoints % 2) or (nPoints2 % 2):
continue
result.append(f)
self.timer.start(0.0)
self.loop.exec_()
if(not self.running):
break
return result
def externalFaces(self, shape):
""" Returns detected external faces.
@param shape Shape where external faces wanted.
@return List of external faces detected.
"""
result = []
faces = shape.Faces
bbox = shape.BoundBox
L = bbox.XMax - bbox.XMin
B = bbox.YMax - bbox.YMin
T = bbox.ZMax - bbox.ZMin
dist = math.sqrt(L*L + B*B + T*T)
msg = QtGui.QApplication.translate("ship_console", "Computing external faces",
None,QtGui.QApplication.UnicodeUTF8)
App.Console.PrintMessage(msg + '...\n')
# Valid/unvalid faces detection loop
for i in range(0,len(faces)):
App.Console.PrintMessage("\t%d / %d\n" % (i+1, len(faces)))
f = faces[i]
# Create a line normal to surface at middle point
u = 0.0
v = 0.0
try:
surf = f.Surface
u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1])
v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1])
except:
cog = f.CenterOfMass
[u,v] = f.Surface.parameter(cog)
p0 = f.valueAt(u,v)
try:
n = f.normalAt(u,v).normalize()
except:
continue
p1 = p0 + n.multiply(1.5*dist)
line = Part.makeLine(p0, p1)
# Look for faces in front of this
nPoints = 0
for j in range(0,len(faces)):
f2 = faces[j]
section = self.lineFaceSection(line, f2)
if len(section) <= 2:
continue
# Add points discarding start and end
nPoints = nPoints + len(section) - 2
# In order to avoid special directions we can modify line
# normal a little bit.
angle = 5
line.rotate(p0,Vector(1,0,0),angle)
line.rotate(p0,Vector(0,1,0),angle)
line.rotate(p0,Vector(0,0,1),angle)
nPoints2 = 0
for j in range(0,len(faces)):
if i == j:
continue
f2 = faces[j]
section = self.lineFaceSection(line, f2)
if len(section) <= 2:
continue
# Add points discarding start and end
nPoints2 = nPoints + len(section) - 2
# If the number of intersection points is pair, is a
# external face. So if we found an odd points intersection,
# face must be discarded.
if (nPoints % 2) or (nPoints2 % 2):
continue
result.append(f)
self.timer.start(0.0)
self.loop.exec_()
if(not self.running):
break
return result
def createTask():
panel = TaskPanel()
Gui.Control.showDialog(panel)
if panel.setupUi():
Gui.Control.closeDialog(panel)
return None
return panel
panel = TaskPanel()
Gui.Control.showDialog(panel)
if panel.setupUi():
Gui.Control.closeDialog(panel)
return None
return panel

View File

@ -30,406 +30,424 @@ import FreeCADGui as Gui
import Instance
from shipUtils import Math
def areas(ship, draft, roll=0.0, trim=0.0, yaw=0.0, n=30):
""" Compute the ship transversal areas.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@param n Number of sections to perform.
@return Transversal areas (every area value is composed by x
coordinate and computed area)
"""
if n < 2:
return []
# We will take a duplicate of ship shape in order to conviniently place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft*Units.Metre.Value))
# Rotations composition is Roll->Trim->Yaw
shape.rotate(Vector(0.0,0.0,0.0), Vector(1.0,0.0,0.0), roll)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,0.0,1.0), yaw)
# Now we need to know the x range of values to perform the sections
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
dx = (xmax - xmin) / (n-1.0)
# Since we are computing in the total length (not in the perpendiculars one),
# we can grant that the starting and ending sections are null
areas = [[xmin/Units.Metre.Value, 0.0]]
# And since we need only face entities, in order to compute sections we will
# create boxes with front face at the desired transversal area position,
# computing the common solid part, dividing it by faces, and getting only
# the desired ones.
App.Console.PrintMessage("Computing transversal areas...\n")
App.Console.PrintMessage("Some Inventor representation errors can be shown, please ignore it.\n")
for i in range(1,n-1):
App.Console.PrintMessage("{0} / {1}\n".format(i, n-2))
x = xmin + i*dx
area = 0.0
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin)
try:
box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin, p)
except:
areas.append([x, area])
continue
# Compute the common part with ship
for s in shape.Solids:
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide the solid by faces and compute only the well placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Place filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
# It is a valid face, so we can add this area
area = area + f.Area/Units.Metre.Value**2
# Destroy the last generated object
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
areas.append([x/Units.Metre.Value, area])
# Last area is equal to zero (see some lines above)
areas.append([xmax/Units.Metre.Value, 0.0])
App.Console.PrintMessage("Done!\n")
return areas
""" Compute the ship transversal areas.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@param n Number of sections to perform.
@return Transversal areas (every area value is composed by x
coordinate and computed area)
"""
if n < 2:
return []
# We will take a duplicate of ship shape in order to conviniently
# manipulate it
shape = ship.Shape.copy()
shape.translate(Vector(0.0, 0.0, -draft * Units.Metre.Value))
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll)
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw)
# Sections distance computation
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
dx = (xmax - xmin) / (n - 1.0)
# Since we are computing the sections in the total length (not in the
# length between perpendiculars), we can grant that the starting and
# ending sections have null area
areas = [[xmin / Units.Metre.Value, 0.0]]
# And since we just need to compute areas we will create boxes with its
# front face at the desired transversal area position, computing the
# common solid part, dividing it by faces, and getting only the desired
# ones.
App.Console.PrintMessage("Computing transversal areas...\n")
App.Console.PrintMessage("Some Inventor representation errors can be"
" shown, please ignore them.\n")
for i in range(1, n - 1):
App.Console.PrintMessage("{0} / {1}\n".format(i, n - 2))
x = xmin + i * dx
area = 0.0
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5 * L, -1.5 * B, bbox.ZMin)
try:
box = Part.makeBox(1.5 * L + x, 3.0 * B, -bbox.ZMin, p)
except:
areas.append([x, area])
continue
# Compute the common part with ship
for s in shape.Solids:
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide the solid by faces and compute only the well placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Place filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
# It is a valid face, so we can add this area
area = area + f.Area / Units.Metre.Value**2
# Destroy the last generated object
App.ActiveDocument.removeObject(
App.ActiveDocument.Objects[-1].Name)
areas.append([x / Units.Metre.Value, area])
# Last area is equal to zero (due to the total length usage)
areas.append([xmax / Units.Metre.Value, 0.0])
App.Console.PrintMessage("Done!\n")
return areas
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0):
""" Compute the ship displacement.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@return [disp, B, Cb], \n
- disp = Ship displacement [ton].
- B = Bouyance center [m].
- Cb = Block coefficient.
@note Bouyance center will returned as a FreeCAD.Vector instance.
@note Returned Bouyance center is in the non modified ship coordinates
"""
# We will take a duplicate of ship shape in order to conviniently place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft*Units.Metre.Value))
# Rotations composition is Roll->Trim->Yaw
shape.rotate(Vector(0.0,0.0,0.0), Vector(1.0,0.0,0.0), roll)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,0.0,1.0), yaw)
# Now we need to know box dimensions
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin)
try:
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin, p)
except:
return [0.0, Vector(), 0.0]
vol = 0.0
cog = Vector()
for solid in shape.Solids:
# Compute the common part with the ship
try:
common = box.common(solid)
except:
continue
# Get the data
vol = vol + common.Volume/Units.Metre.Value**3
for s in common.Solids:
sCoG = s.CenterOfMass
cog.x = cog.x + sCoG.x*s.Volume/Units.Metre.Value**4
cog.y = cog.y + sCoG.y*s.Volume/Units.Metre.Value**4
cog.z = cog.z + sCoG.z*s.Volume/Units.Metre.Value**4
cog.x = cog.x / vol
cog.y = cog.y / vol
cog.z = cog.z / vol
Vol = L*B*abs(bbox.ZMin)/Units.Metre.Value**3
# Undo transformations
B = Vector()
B.x = cog.x*math.cos(math.radians(-yaw)) - cog.y*math.sin(math.radians(-yaw))
B.y = cog.x*math.sin(math.radians(-yaw)) + cog.y*math.cos(math.radians(-yaw))
B.z = cog.z
cog.x = B.x*math.cos(math.radians(-trim)) - B.z*math.sin(math.radians(-trim))
cog.y = B.y
cog.z = B.x*math.sin(math.radians(-trim)) + B.z*math.cos(math.radians(-trim))
B.x = cog.x
B.y = cog.y*math.cos(math.radians(-roll)) - cog.z*math.sin(math.radians(-roll))
B.z = cog.y*math.sin(math.radians(-roll)) + cog.z*math.cos(math.radians(-roll))
B.z = B.z + draft
# Return data
dens = 1.025 # [tons/m3], salt water
return [dens*vol, B, vol/Vol]
""" Compute the ship displacement.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@return [disp, B, Cb], \n
- disp = Ship displacement [ton].
- B = Bouyance center [m].
- Cb = Block coefficient.
@note Bouyance center will returned as a FreeCAD.Vector instance.
@note Returned Bouyance center is in the non modified ship coordinates
"""
# We will take a duplicate of ship shape in order to conviniently
# manipulate it
shape = ship.Shape.copy()
shape.translate(Vector(0.0, 0.0, -draft * Units.Metre.Value))
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll)
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw)
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the "sea" box to intersect the ship
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin)
try:
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin, p)
except:
return [0.0, Vector(), 0.0]
vol = 0.0
cog = Vector()
for solid in shape.Solids:
# Compute the common part of the "sea" with the ship
try:
common = box.common(solid)
except:
continue
# Get the data
vol = vol + common.Volume / Units.Metre.Value**3
for s in common.Solids:
sCoG = s.CenterOfMass
cog.x = cog.x + sCoG.x * s.Volume / Units.Metre.Value**4
cog.y = cog.y + sCoG.y * s.Volume / Units.Metre.Value**4
cog.z = cog.z + sCoG.z * s.Volume / Units.Metre.Value**4
cog.x = cog.x / vol
cog.y = cog.y / vol
cog.z = cog.z / vol
Vol = L * B * abs(bbox.ZMin) / Units.Metre.Value**3
# Undo the transformations
B = Vector()
B.x = cog.x * math.cos(math.radians(-yaw)) - \
cog.y * math.sin(math.radians(-yaw))
B.y = cog.x * math.sin(math.radians(-yaw)) + \
cog.y * math.cos(math.radians(-yaw))
B.z = cog.z
cog.x = B.x * math.cos(math.radians(-trim)) - \
B.z * math.sin(math.radians(-trim))
cog.y = B.y
cog.z = B.x * math.sin(math.radians(-trim)) + \
B.z * math.cos(math.radians(-trim))
B.x = cog.x
B.y = cog.y * math.cos(math.radians(-roll)) - \
cog.z * math.sin(math.radians(-roll))
B.z = cog.y * math.sin(math.radians(-roll)) + \
cog.z * math.cos(math.radians(-roll))
B.z = B.z + draft
# Return the computed data
dens = 1.025 # [tons/m3], salt water
return [dens*vol, B, vol/Vol]
def wettedArea(shape, draft, trim):
""" Calculate wetted ship area.
@param shape Ship external faces instance.
@param draft Draft.
@param trim Trim in degrees.
@return Wetted ship area.
"""
area = 0.0
nObjects = 0
# We will take a duplicate of ship shape in order to place it
shape = shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
for f in shape.Faces:
# Get solids intersection
try:
common = box.common(f)
except:
continue
area = area + common.Area
return area
""" Calculate wetted ship area.
@param shape Ship external faces instance.
@param draft Draft.
@param trim Trim in degrees.
@return Wetted ship area.
"""
area = 0.0
nObjects = 0
shape = shape.copy()
shape.translate(Vector(0.0, 0.0, -draft))
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the "sea" box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
for f in shape.Faces:
try:
common = box.common(f)
except:
continue
area = area + common.Area
return area
def moment(ship, draft, trim, disp, xcb):
""" Calculate triming 1cm ship moment.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@param disp Displacement at selected draft and trim.
@param xcb Bouyance center at selected draft and trim.
@return Moment to trim ship 1cm (ton m).
@note Moment is positive when produce positive trim.
"""
factor = 10.0
angle = factor*math.degrees(math.atan2(0.01,0.5*ship.Length))
newTrim = trim + angle
data = displacement(ship,draft,0.0,newTrim,0.0)
mom0 = -disp*xcb
mom1 = -data[0]*data[1].x
return (mom1 - mom0) / factor
""" Calculate triming 1cm ship moment.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@param disp Displacement at selected draft and trim.
@param xcb Bouyance center at selected draft and trim.
@return Moment to trim ship 1cm (ton m).
@note Moment is positive when produce positive trim.
"""
factor = 10.0
angle = factor * math.degrees(math.atan2(0.01, 0.5 * ship.Length))
newTrim = trim + angle
data = displacement(ship, draft, 0.0, newTrim, 0.0)
mom0 = -disp * xcb
mom1 = -data[0] * data[1].x
return (mom1 - mom0) / factor
def FloatingArea(ship, draft, trim):
""" Calculate ship floating area.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@return Ship floating area, and floating coefficient.
"""
area = 0.0
cf = 0.0
maxX = 0.0
minX = 0.0
maxY = 0.0
minY = 0.0
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
maxX = bbox.XMin
minX = bbox.XMax
maxY = bbox.YMin
minY = bbox.YMax
for s in shape.Solids:
# Get solids intersection
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide by faces and compute only section placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.ZMax - faceBounds.ZMin > 0.00001:
continue
# Place filter
if abs(faceBounds.ZMax) > 0.00001:
continue
# Valid face, compute area
area = area + f.Area
maxX = max(maxX, faceBounds.XMax)
minX = min(minX, faceBounds.XMin)
maxY = max(maxY, faceBounds.YMax)
minY = min(minY, faceBounds.YMin)
# Destroy last object generated
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dx = maxX - minX
dy = maxY - minY
if dx*dy > 0.0:
cf = area / (dx*dy)
return [area, cf]
""" Calculate ship floating area.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@return Ship floating area, and floating coefficient.
"""
area = 0.0
cf = 0.0
maxX = 0.0
minX = 0.0
maxY = 0.0
minY = 0.0
shape = ship.Shape.copy()
shape.translate(Vector(0.0, 0.0, -draft))
shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the "sea" box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
maxX = bbox.XMin
minX = bbox.XMax
maxY = bbox.YMin
minY = bbox.YMax
for s in shape.Solids:
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute the object adding it to the scene. OpenCASCADE must be
# performing an internal tesellation doing that
try:
Part.show(common)
except:
continue
# Divide the solid by faces and filter the well placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.ZMax - faceBounds.ZMin > 0.00001:
continue
# Position filter
if abs(faceBounds.ZMax) > 0.00001:
continue
area = area + f.Area
maxX = max(maxX, faceBounds.XMax)
minX = min(minX, faceBounds.XMin)
maxY = max(maxY, faceBounds.YMax)
minY = min(minY, faceBounds.YMin)
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dx = maxX - minX
dy = maxY - minY
if dx*dy > 0.0:
cf = area / (dx * dy)
return [area, cf]
def BMT(ship, draft, trim=0.0):
""" Calculate ship Bouyance center transversal distance.
@param ship Ship instance.
@param draft Ship draft.
@param trim Ship trim angle.
@return BM Bouyance to metacenter height [m].
"""
nRoll = 2
maxRoll = 7.0
B0 = displacement(ship,draft,0.0,trim,0.0)[1]
BM = 0.0
for i in range(0,nRoll):
roll = (maxRoll/nRoll)*(i+1)
B1 = displacement(ship,draft,roll,trim,0.0)[1]
# * M
# / \
# / \ BM ==|> BM = (BB/2) / sin(alpha/2)
# / \
# *-------*
# BB
BB = [B1.y - B0.y, B1.z - B0.z]
BB = math.sqrt(BB[0]*BB[0] + BB[1]*BB[1])
BM = BM + 0.5*BB/math.sin(math.radians(0.5*roll)) / nRoll # nRoll is the weight function
return BM
""" Calculate ship Bouyance center transversal distance.
@param ship Ship instance.
@param draft Ship draft.
@param trim Ship trim angle.
@return BM Bouyance to metacenter height [m].
"""
nRoll = 2
maxRoll = 7.0
B0 = displacement(ship, draft, 0.0, trim, 0.0)[1]
BM = 0.0
for i in range(nRoll):
roll = (maxRoll / nRoll)*(i + 1)
B1 = displacement(ship, draft, roll, trim, 0.0)[1]
# * M
# / \
# / \ BM ==|> BM = (BB/2) / sin(alpha/2)
# / \
# *-------*
# BB
BB = [B1.y - B0.y, B1.z - B0.z]
BB = math.sqrt(BB[0] * BB[0] + BB[1] * BB[1])
# nRoll is acting as the weight function
BM = BM + 0.5 * BB / math.sin(math.radians(0.5 * roll)) / nRoll
return BM
def mainFrameCoeff(ship, draft):
""" Calculate main frame coefficient.
@param ship Selected ship instance
@param draft Draft.
@return Main frame coefficient
"""
cm = 0.0
maxY = 0.0
minY = 0.0
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
x = 0.0
area = 0.0
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin + 1.0, p)
maxY = bbox.YMin
minY = bbox.YMax
# Compute common part with ship
for s in shape.Solids:
# Get solids intersection
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide by faces and compute only section placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Place filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
# Valid face, compute area
area = area + f.Area
maxY = max(maxY, faceBounds.YMax)
minY = min(minY, faceBounds.YMin)
# Destroy last object generated
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dy = maxY - minY
if dy*draft > 0.0:
cm = area / (dy*draft)
return cm
""" Calculate main frame coefficient.
@param ship Selected ship instance
@param draft Draft.
@return Main frame coefficient
"""
cm = 0.0
maxY = 0.0
minY = 0.0
shape = ship.Shape.copy()
shape.translate(Vector(0.0, 0.0, -draft))
x = 0.0
area = 0.0
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the "sea" box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin + 1.0, p)
maxY = bbox.YMin
minY = bbox.YMax
for s in shape.Solids:
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute the object adding it to the scene. OpenCASCADE must be
# performing an internal tesellation doing that
try:
Part.show(common)
except:
continue
# Divide the solid by faces and filter the well placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Position filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
area = area + f.Area
maxY = max(maxY, faceBounds.YMax)
minY = min(minY, faceBounds.YMin)
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dy = maxY - minY
if dy * draft > 0.0:
cm = area / (dy * draft)
return cm
class Point:
""" Hydrostatics point, that conatins: \n
draft Ship draft [m]. \n
trim Ship trim [deg]. \n
disp Ship displacement [ton]. \n
xcb Bouyance center X coordinate [m].
wet Wetted ship area [m2].
mom Triming 1cm ship moment [ton m].
farea Floating area [m2].
KBt Transversal KB height [m].
BMt Transversal BM height [m].
Cb Block coefficient.
Cf Floating coefficient.
Cm Main frame coefficient.
@note Moment is positive when produce positive trim.
"""
def __init__(self, ship, faces, draft, trim):
""" Use all hydrostatics tools to define a hydrostatics
point.
@param ship Selected ship instance
@param faces Ship external faces
@param draft Draft.
@param trim Trim in degrees.
"""
# Hydrostatics computation
dispData = displacement(ship,draft,0.0,trim,0.0)
if not faces:
wet = 0.0
else:
wet = wettedArea(faces,draft,trim)
mom = moment(ship,draft,trim,dispData[0],dispData[1].x)
farea = FloatingArea(ship,draft,trim)
bm = BMT(ship,draft,trim)
cm = mainFrameCoeff(ship,draft)
# Store final data
self.draft = draft
self.trim = trim
self.disp = dispData[0]
self.xcb = dispData[1].x
self.wet = wet
self.farea = farea[0]
self.mom = mom
self.KBt = dispData[1].z
self.BMt = bm
self.Cb = dispData[2]
self.Cf = farea[1]
self.Cm = cm
""" Hydrostatics point, that conatins: \n
draft Ship draft [m]. \n
trim Ship trim [deg]. \n
disp Ship displacement [ton]. \n
xcb Bouyance center X coordinate [m].
wet Wetted ship area [m2].
mom Triming 1cm ship moment [ton m].
farea Floating area [m2].
KBt Transversal KB height [m].
BMt Transversal BM height [m].
Cb Block coefficient.
Cf Floating coefficient.
Cm Main frame coefficient.
@note Moment is positive when produce positive trim.
"""
def __init__(self, ship, faces, draft, trim):
""" Use all hydrostatics tools to define a hydrostatics
point.
@param ship Selected ship instance
@param faces Ship external faces
@param draft Draft.
@param trim Trim in degrees.
"""
# Hydrostatics computation
dispData = displacement(ship, draft, 0.0, trim, 0.0)
if not faces:
wet = 0.0
else:
wet = wettedArea(faces, draft, trim)
mom = moment(ship, draft, trim, dispData[0], dispData[1].x)
farea = FloatingArea(ship, draft, trim)
bm = BMT(ship, draft, trim)
cm = mainFrameCoeff(ship, draft)
# Store final data
self.draft = draft
self.trim = trim
self.disp = dispData[0]
self.xcb = dispData[1].x
self.wet = wet
self.farea = farea[0]
self.mom = mom
self.KBt = dispData[1].z
self.BMt = bm
self.Cb = dispData[2]
self.Cf = farea[1]
self.Cm = cm

View File

@ -21,11 +21,9 @@
#* *
#***************************************************************************
import FreeCAD
import FreeCADGui
from PyQt4 import QtGui,QtCore
import TaskPanel
def load():
""" Loads the tool """
TaskPanel.createTask()
""" Loads the tool """
TaskPanel.createTask()