diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index c31ce3921..2777f1f70 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -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()) diff --git a/src/Mod/Ship/ShipGui.py b/src/Mod/Ship/ShipGui.py index 08a98c0e9..4a56871a6 100644 --- a/src/Mod/Ship/ShipGui.py +++ b/src/Mod/Ship/ShipGui.py @@ -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()) diff --git a/src/Mod/Ship/shipHydrostatics/PlotAux.py b/src/Mod/Ship/shipHydrostatics/PlotAux.py index a71b50b23..e8e137700 100644 --- a/src/Mod/Ship/shipHydrostatics/PlotAux.py +++ b/src/Mod/Ship/shipHydrostatics/PlotAux.py @@ -1,371 +1,394 @@ #*************************************************************************** -#* * -#* Copyright (c) 2011, 2012 * -#* Jose Luis Cercos Pita * -#* * +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * +#* 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 diff --git a/src/Mod/Ship/shipHydrostatics/TaskPanel.py b/src/Mod/Ship/shipHydrostatics/TaskPanel.py index 231613398..ea45dc2a3 100644 --- a/src/Mod/Ship/shipHydrostatics/TaskPanel.py +++ b/src/Mod/Ship/shipHydrostatics/TaskPanel.py @@ -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 diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py index ab4cf52e5..d369ea77b 100644 --- a/src/Mod/Ship/shipHydrostatics/Tools.py +++ b/src/Mod/Ship/shipHydrostatics/Tools.py @@ -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 diff --git a/src/Mod/Ship/shipHydrostatics/__init__.py b/src/Mod/Ship/shipHydrostatics/__init__.py index cd1772942..812df3a4a 100644 --- a/src/Mod/Ship/shipHydrostatics/__init__.py +++ b/src/Mod/Ship/shipHydrostatics/__init__.py @@ -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()