Updated the hydrostatics tool
This commit is contained in:
parent
1801857108
commit
e7c3e264ff
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue
Block a user