Merge remote-tracking branch 'remotes/origin/freecad-ship'
This commit is contained in:
commit
27eb32238a
|
@ -21,6 +21,9 @@ SET(ShipIcons_SRCS
|
|||
Icons/Ico.png
|
||||
Icons/Ico.xcf
|
||||
Icons/Ico.xpm
|
||||
Icons/LoadIco.png
|
||||
Icons/LoadIco.xcf
|
||||
Icons/LoadIco.xpm
|
||||
Icons/OutlineDrawIco.png
|
||||
Icons/OutlineDrawIco.xcf
|
||||
Icons/OutlineDrawIco.xpm
|
||||
|
@ -32,6 +35,20 @@ SET(ShipIcons_SRCS
|
|||
)
|
||||
SOURCE_GROUP("shipicons" FILES ${ShipIcons_SRCS})
|
||||
|
||||
SET(ShipExamples_SRCS
|
||||
Examples/s60.fcstd
|
||||
Examples/barehull5415.fcstd
|
||||
Examples/s60_katamaran.fcstd
|
||||
)
|
||||
SOURCE_GROUP("shipexamples" FILES ${ShipExamples_SRCS})
|
||||
|
||||
SET(ShipLoadExample_SRCS
|
||||
shipLoadExample/__init__.py
|
||||
shipLoadExample/TaskPanel.py
|
||||
shipLoadExample/TaskPanel.ui
|
||||
)
|
||||
SOURCE_GROUP("shiploadexample" FILES ${ShipLoadExample_SRCS})
|
||||
|
||||
SET(ShipCreateShip_SRCS
|
||||
shipCreateShip/__init__.py
|
||||
shipCreateShip/Preview.py
|
||||
|
@ -51,6 +68,7 @@ SOURCE_GROUP("shipoutlinedraw" FILES ${ShipOutlineDraw_SRCS})
|
|||
|
||||
SET(ShipAreasCurve_SRCS
|
||||
shipAreasCurve/__init__.py
|
||||
shipAreasCurve/Plot.py
|
||||
shipAreasCurve/Preview.py
|
||||
shipAreasCurve/TaskPanel.py
|
||||
shipAreasCurve/TaskPanel.ui
|
||||
|
@ -59,6 +77,9 @@ SOURCE_GROUP("shipareascurve" FILES ${ShipAreasCurve_SRCS})
|
|||
|
||||
SET(ShipHydrostatics_SRCS
|
||||
shipHydrostatics/__init__.py
|
||||
shipHydrostatics/Plot.py
|
||||
shipHydrostatics/TaskPanel.py
|
||||
shipHydrostatics/TaskPanel.ui
|
||||
shipHydrostatics/Tools.py
|
||||
)
|
||||
SOURCE_GROUP("shiphydrostatics" FILES ${ShipHydrostatics_SRCS})
|
||||
|
@ -71,13 +92,13 @@ SET(ShipUtils_SRCS
|
|||
)
|
||||
SOURCE_GROUP("shiputils" FILES ${ShipUtils_SRCS})
|
||||
|
||||
SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS})
|
||||
SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS})
|
||||
|
||||
ADD_CUSTOM_TARGET(Ship ALL
|
||||
SOURCES ${all_files}
|
||||
)
|
||||
|
||||
fc_copy_sources(Ship "${CMAKE_BINARY_DIR}/Mod/Ship" ${all_files})
|
||||
fc_copy_sources(Mod/Ship "${CMAKE_BINARY_DIR}/Mod/Ship" ${all_files})
|
||||
|
||||
INSTALL(
|
||||
FILES
|
||||
|
@ -85,6 +106,18 @@ INSTALL(
|
|||
DESTINATION
|
||||
Mod/Ship/Icons
|
||||
)
|
||||
INSTALL(
|
||||
FILES
|
||||
${ShipExamples_SRCS}
|
||||
DESTINATION
|
||||
Mod/Ship/Examples
|
||||
)
|
||||
INSTALL(
|
||||
FILES
|
||||
${ShipLoadExample_SRCS}
|
||||
DESTINATION
|
||||
Mod/Ship/shipLoadExample
|
||||
)
|
||||
INSTALL(
|
||||
FILES
|
||||
${ShipCreateShip_SRCS}
|
||||
|
|
BIN
src/Mod/Ship/Examples/barehull5415.fcstd
Normal file
BIN
src/Mod/Ship/Examples/barehull5415.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Ship/Examples/s60.fcstd
Normal file
BIN
src/Mod/Ship/Examples/s60.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Ship/Examples/s60_katamaran.fcstd
Normal file
BIN
src/Mod/Ship/Examples/s60_katamaran.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Ship/Icons/LoadIco.png
Normal file
BIN
src/Mod/Ship/Icons/LoadIco.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
src/Mod/Ship/Icons/LoadIco.xcf
Normal file
BIN
src/Mod/Ship/Icons/LoadIco.xcf
Normal file
Binary file not shown.
1985
src/Mod/Ship/Icons/LoadIco.xpm
Normal file
1985
src/Mod/Ship/Icons/LoadIco.xpm
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -32,11 +32,11 @@ class ShipWorkbench ( Workbench ):
|
|||
|
||||
def Initialize(self):
|
||||
# ToolBar
|
||||
list = ["Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve"]
|
||||
list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"]
|
||||
self.appendToolbar("Ship design",list)
|
||||
|
||||
# Menu
|
||||
list = ["Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve"]
|
||||
list = ["Ship_LoadExample", "Ship_CreateShip", "Ship_OutlineDraw", "Ship_AreasCurve", "Ship_Hydrostatics"]
|
||||
self.appendMenu("Ship design",list)
|
||||
|
||||
Gui.addWorkbench(ShipWorkbench())
|
||||
|
|
|
@ -120,7 +120,7 @@ class Ship:
|
|||
for j in range(0,nP):
|
||||
z = z0 + j*dz
|
||||
rX = x1 - x0
|
||||
rY = y1 - y0
|
||||
rY = max(y1 - y0, abs(y1), abs(y0))
|
||||
planes.append(Part.makePlane(4*rX,4*rY,Base.Vector(-2*rX,-2*rY,z),Base.Vector(0,0,1)))
|
||||
# Division are performed at x axis
|
||||
dx = (x1 - x0) / (nS - 1.0)
|
||||
|
@ -134,12 +134,13 @@ class Ship:
|
|||
wires = shape.slice(Vector(1.0,0.0,0.0), x)
|
||||
if not wires:
|
||||
if (i != 0) or (i != nS-1):
|
||||
msg = 'Found empty section at x=%g\n'
|
||||
msg = 'Found empty section at x=%g\n' % (x)
|
||||
msg = Translator.translate(msg)
|
||||
FreeCAD.Console.PrintWarning(msg)
|
||||
FreeCAD.Console.PrintWarning('\tThis may happens if a bad defined (or really complex) surface has been provided.\n')
|
||||
FreeCAD.Console.PrintWarning('\tPlease, ensure that this section is correct, or fix surfaces and create a new ship.\n')
|
||||
nPoints.append(0)
|
||||
continue
|
||||
# Desarrollate wires into edges list
|
||||
edges = []
|
||||
for j in range(0,len(wires)):
|
||||
|
@ -148,21 +149,25 @@ class Ship:
|
|||
edges.append(wire[k])
|
||||
# Slice curves to get points (Length based)
|
||||
points = []
|
||||
for j in range(0,len(edges)):
|
||||
for k in range(0,nP):
|
||||
for k in range(0,nP):
|
||||
planePoints = []
|
||||
for j in range(0,len(edges)):
|
||||
aux = self.lineFaceSection(edges[j], planes[k])
|
||||
if not aux:
|
||||
points.append(Vector(x,0,z0 + k*dz))
|
||||
for l in range(0,len(aux)):
|
||||
points.append(Vector(aux[l].X, aux[l].Y, aux[l].Z))
|
||||
# Sort section points at Y direction
|
||||
aux = []
|
||||
for j in range(0,len(points)):
|
||||
aux.append(points[j].y)
|
||||
aux.sort()
|
||||
for j in range(0,len(points)):
|
||||
section.append(Vector(points[j].x, aux[j], points[j].z))
|
||||
planePoints.append(Vector(aux[l].X, aux[l].Y, aux[l].Z))
|
||||
if not planePoints: # No section found, symmetry plane point will used
|
||||
planePoints.append(Vector(x,0,z0 + k*dz))
|
||||
# Get Y coordinates
|
||||
auxY = []
|
||||
for l in range(0,len(planePoints)):
|
||||
auxY.append(planePoints[l].y)
|
||||
# Sort them
|
||||
auxY.sort()
|
||||
# And store
|
||||
for l in range(0,len(planePoints)):
|
||||
points.append(Vector(planePoints[l].x, auxY[l], planePoints[l].z))
|
||||
# Store points
|
||||
section = points[:]
|
||||
nPoints.append(len(section))
|
||||
for j in range(0,len(section)):
|
||||
mSections.append(section[j])
|
||||
|
@ -172,6 +177,9 @@ class Ship:
|
|||
self.obj.nPoints = nPoints[:]
|
||||
self.obj.xSection = xSection[:]
|
||||
self.obj.mSections = mSections[:]
|
||||
msg = '%d Discretization points performed\n' % (len(mSections))
|
||||
msg = Translator.translate(msg)
|
||||
FreeCAD.Console.PrintMessage(msg)
|
||||
|
||||
class ViewProviderShip:
|
||||
def __init__(self, obj):
|
||||
|
|
|
@ -22,6 +22,9 @@ nobase_data_DATA = \
|
|||
Icons/Ico.png \
|
||||
Icons/Ico.xcf \
|
||||
Icons/Ico.xpm \
|
||||
Icons/LoadIco.png \
|
||||
Icons/LoadIco.xcf \
|
||||
Icons/LoadIco.xpm \
|
||||
Icons/OutlineDrawIco.png \
|
||||
Icons/OutlineDrawIco.xcf \
|
||||
Icons/OutlineDrawIco.xpm \
|
||||
|
@ -30,6 +33,12 @@ nobase_data_DATA = \
|
|||
Icons/ReparametrizeIco.xpm \
|
||||
Icons/Ship.xcf \
|
||||
Icons/Ship.xpm \
|
||||
Examples/s60.fcstd \
|
||||
Examples/barehull5415.fcstd \
|
||||
Examples/s60_katamaran.fcstd \
|
||||
shipLoadExample/__init__.py \
|
||||
shipLoadExample/TaskPanel.py \
|
||||
shipLoadExample/TaskPanel.ui \
|
||||
shipCreateShip/__init__.py \
|
||||
shipCreateShip/Preview.py \
|
||||
shipCreateShip/TaskPanel.py \
|
||||
|
@ -40,10 +49,14 @@ nobase_data_DATA = \
|
|||
shipOutlineDraw/TaskPanel.py \
|
||||
shipOutlineDraw/TaskPanel.ui \
|
||||
shipAreasCurve/__init__.py \
|
||||
shipAreasCurve/Plot.py \
|
||||
shipAreasCurve/Preview.py \
|
||||
shipAreasCurve/TaskPanel.py \
|
||||
shipAreasCurve/TaskPanel.ui \
|
||||
shipHydrostatics/__init__.py \
|
||||
shipHydrostatics/Plot.py \
|
||||
shipHydrostatics/TaskPanel.py \
|
||||
shipHydrostatics/TaskPanel.ui \
|
||||
shipHydrostatics/Tools.py \
|
||||
shipUtils/__init__.py \
|
||||
shipUtils/Math.py \
|
||||
|
|
|
@ -24,6 +24,18 @@
|
|||
from PyQt4 import QtCore, QtGui
|
||||
import FreeCAD, FreeCADGui, os
|
||||
|
||||
class LoadExample:
|
||||
def Activated(self):
|
||||
import shipLoadExample
|
||||
shipLoadExample.load()
|
||||
|
||||
def GetResources(self):
|
||||
from shipUtils import Paths, Translator
|
||||
IconPath = Paths.iconsPath() + "/LoadIco.png"
|
||||
MenuText = str(Translator.translate('Load an example ship geometry'))
|
||||
ToolTip = str(Translator.translate('Load an example ship geometry able to be converted into a ship.'))
|
||||
return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
|
||||
|
||||
class CreateShip:
|
||||
def Activated(self):
|
||||
import shipCreateShip
|
||||
|
@ -59,7 +71,21 @@ class AreasCurve:
|
|||
MenuText = str(Translator.translate('Areas curve'))
|
||||
ToolTip = str(Translator.translate('Plot transversal areas curve'))
|
||||
return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
|
||||
|
||||
class Hydrostatics:
|
||||
def Activated(self):
|
||||
import shipHydrostatics
|
||||
shipHydrostatics.load()
|
||||
|
||||
def GetResources(self):
|
||||
from shipUtils import Paths, Translator
|
||||
IconPath = Paths.iconsPath() + "/HydrostaticsIco.png"
|
||||
MenuText = str(Translator.translate('Hydrostatics'))
|
||||
ToolTip = str(Translator.translate('Plot ship hydrostatics'))
|
||||
return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip}
|
||||
|
||||
FreeCADGui.addCommand('Ship_LoadExample', LoadExample())
|
||||
FreeCADGui.addCommand('Ship_CreateShip', CreateShip())
|
||||
FreeCADGui.addCommand('Ship_OutlineDraw', OutlineDraw())
|
||||
FreeCADGui.addCommand('Ship_AreasCurve', AreasCurve())
|
||||
FreeCADGui.addCommand('Ship_Hydrostatics', Hydrostatics())
|
||||
|
|
211
src/Mod/Ship/shipAreasCurve/Plot.py
Normal file
211
src/Mod/Ship/shipAreasCurve/Plot.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2011, 2012 *
|
||||
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import os
|
||||
# FreeCAD modules
|
||||
import FreeCAD,FreeCADGui
|
||||
from FreeCAD import Part, Base
|
||||
from FreeCAD import Image, ImageGui
|
||||
# FreeCADShip modules
|
||||
from shipUtils import Paths, Translator
|
||||
|
||||
header = """ #################################################################
|
||||
|
||||
##### #### ### #### ##### # # ### ####
|
||||
# # # # # # # # # # # #
|
||||
# ## #### #### # # # # # # # # # # #
|
||||
#### # # # # # # # ##### # # ## ## ##### # ####
|
||||
# # #### #### # # # # # # # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # #### #### ### # # #### ##### # # ### #
|
||||
|
||||
#################################################################
|
||||
"""
|
||||
|
||||
class Plot(object):
|
||||
def __init__(self, x, y, disp, xcb, ship):
|
||||
""" Constructor. performs plot and show it (Using pyxplot).
|
||||
@param x X coordinates.
|
||||
@param y Transversal areas.
|
||||
@param disp Ship displacement.
|
||||
@param xcb Bouyancy center length.
|
||||
@param ship Active ship instance.
|
||||
"""
|
||||
if self.createDirectory():
|
||||
return
|
||||
if self.saveData(x,y,ship):
|
||||
return
|
||||
if self.saveLayout(x,y,disp,xcb,ship):
|
||||
return
|
||||
if self.execute():
|
||||
return
|
||||
ImageGui.open(self.path + 'areas.png')
|
||||
|
||||
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 = Translator.translate("Can't create '" + self.path + "' folder.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return False
|
||||
|
||||
def saveData(self,x,y,ship):
|
||||
""" Write data file.
|
||||
@param x X coordinates.
|
||||
@param y Transversal areas.
|
||||
@param ship Active ship instance.
|
||||
@return True if error happens.
|
||||
"""
|
||||
# Open the file
|
||||
filename = self.path + 'areas.dat'
|
||||
try:
|
||||
Output = open(filename, "w")
|
||||
except IOError:
|
||||
msg = Translator.translate("Can't write '" + filename + "' file.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
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: X coordiante [m]\n")
|
||||
Output.write(" # 2: Transversal area [m2]\n")
|
||||
Output.write(" # 3: X FP coordinate [m]\n")
|
||||
Output.write(" # 4: Y FP coordinate (bounds in order to draw it)\n")
|
||||
Output.write(" # 3: X AP coordinate [m]\n")
|
||||
Output.write(" # 4: Y AP coordinate (bounds in order to draw it)\n")
|
||||
Output.write(" #\n")
|
||||
Output.write(" #################################################################\n")
|
||||
# Get perpendiculars data
|
||||
Lpp = ship.Length
|
||||
FPx = 0.5*Lpp
|
||||
APx = -0.5*Lpp
|
||||
maxArea = max(y)
|
||||
# Print data
|
||||
if len(x) < 2:
|
||||
msg = Translator.translate("Not enough data to plot.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
string = "%f %f %f %f %f %f\n" % (x[0], y[0], FPx, 0.0, APx, 0.0)
|
||||
Output.write(string)
|
||||
for i in range(1, len(x)):
|
||||
string = "%f %f %f %f %f %f\n" % (x[i], y[i], FPx, maxArea, APx, maxArea)
|
||||
Output.write(string)
|
||||
# Close file
|
||||
Output.close()
|
||||
self.dataFile = filename
|
||||
msg = Translator.translate("Data saved at '" + self.dataFile + "'.\n")
|
||||
FreeCAD.Console.PrintMessage(msg)
|
||||
return False
|
||||
|
||||
def saveLayout(self, x, y, disp, xcb, ship):
|
||||
""" Prints the data output.
|
||||
@param x X coordinates.
|
||||
@param y Transversal areas.
|
||||
@param disp Ship displacement.
|
||||
@param xcb Bouyancy center length.
|
||||
@param ship Active ship instance.
|
||||
@return True if error happens.
|
||||
"""
|
||||
filename = self.path + 'areas.pyxplot'
|
||||
# Open the file
|
||||
try:
|
||||
Output = open(filename, "w")
|
||||
except IOError:
|
||||
msg = Translator.translate("Can't write '" + filename + "' file.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
# Write header
|
||||
Output.write(header)
|
||||
Output.write(" #\n")
|
||||
Output.write(" # File automatically exported by FreeCAD-Ship\n")
|
||||
Output.write(" # This file contains a script to plot transversal areas curve.\n")
|
||||
Output.write(" # To use it execute:\n")
|
||||
Output.write(" #\n")
|
||||
Output.write(" # pyxplot %s\n" % (filename))
|
||||
Output.write(" #\n")
|
||||
Output.write(" #################################################################\n")
|
||||
# Write general options for hydrostatics
|
||||
Output.write("set numeric display latex\n")
|
||||
Output.write("set output '%s'\n" % (self.path + 'areas.eps'))
|
||||
Output.write("set nokey\n")
|
||||
Output.write("set grid\n")
|
||||
Output.write("# X axis\n")
|
||||
Output.write("set xlabel 'x / $m$'\n")
|
||||
Output.write("set xtic\n")
|
||||
Output.write("# Y axis\n")
|
||||
Output.write("set ylabel 'Area / $m^2$'\n")
|
||||
Output.write("set ytic\n")
|
||||
Output.write("# Line styles\n")
|
||||
Output.write("set style 1 line linetype 1 linewidth 1 colour rgb (0):(0):(0)\n")
|
||||
Output.write("set style 2 line linetype 1 linewidth 2 colour rgb (0):(0):(0)\n")
|
||||
# Get perpendiculars data
|
||||
Lpp = ship.Length
|
||||
FPx = 0.5*Lpp
|
||||
APx = -0.5*Lpp
|
||||
maxArea = max(y)
|
||||
# Perpendicular labels
|
||||
Output.write("# Perpendiculars labels\n")
|
||||
Output.write("set label (1) AP %f,%f\n" % (APx+0.01*Lpp, 0.01*maxArea))
|
||||
Output.write("set label (2) AP %f,%f\n" % (APx+0.01*Lpp, 0.95*maxArea))
|
||||
Output.write("set label (3) FP %f,%f\n" % (FPx+0.01*Lpp, 0.01*maxArea))
|
||||
Output.write("set label (4) FP %f,%f\n" % (FPx+0.01*Lpp, 0.95*maxArea))
|
||||
# Additional data
|
||||
Output.write("# Additional data\n")
|
||||
Output.write("set label (5) 'XCB = %g m' %f,%f\n" % (xcb, -0.25*Lpp, 0.25*maxArea))
|
||||
Output.write("set label (6) 'Maximum area = %g m2' %f,%f\n" % (maxArea, -0.25*Lpp, 0.15*maxArea))
|
||||
Output.write("set label (7) 'Displacement = %g tons' %f,%f\n" % (disp, -0.25*Lpp, 0.05*maxArea))
|
||||
# Write plot call
|
||||
Output.write("# Plot\n")
|
||||
Output.write("plot '%s' using 1:2 title 'Transversal areas' axes x1y1 with lines style 1, \\\n" % (self.dataFile))
|
||||
Output.write(" '%s' using 3:4 title 'FP' axes x1y1 with lines style 2, \\\n" % (self.dataFile))
|
||||
Output.write(" '%s' using 5:6 title 'AP' axes x1y1 with lines style 2\n" % (self.dataFile))
|
||||
# Close file
|
||||
self.layoutFile = filename
|
||||
Output.close()
|
||||
return False
|
||||
|
||||
def execute(self):
|
||||
""" Calls pyxplot in order to plot an save an image.
|
||||
@return True if error happens.
|
||||
"""
|
||||
filename = self.path + 'areas'
|
||||
comm = "pyxplot %s" % (self.layoutFile)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute pyxplot. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Plot will not generated\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
comm = "gs -r300 -dEPSCrop -dTextAlphaBits=4 -sDEVICE=png16m -sOutputFile=%s.png -dBATCH -dNOPAUSE %s.eps" % (filename,filename)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute ghostscript. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Generated image will not converted to png\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
return False
|
|
@ -28,7 +28,7 @@ import FreeCADGui as Gui
|
|||
# Qt library
|
||||
from PyQt4 import QtGui,QtCore
|
||||
# Module
|
||||
import Preview
|
||||
import Preview, Plot
|
||||
import Instance
|
||||
from shipUtils import Paths, Translator
|
||||
from surfUtils import Geometry
|
||||
|
@ -44,6 +44,13 @@ class TaskPanel:
|
|||
if not self.ship:
|
||||
return False
|
||||
self.save()
|
||||
# Plot data
|
||||
data = Hydrostatics.Displacement(self.ship,self.form.draft.value(),self.form.trim.value())
|
||||
x = self.ship.xSection[:]
|
||||
y = data[0]
|
||||
disp = data[1]
|
||||
xcb = data[2]
|
||||
Plot.Plot(x,y,disp,xcb, self.ship)
|
||||
self.preview.clean()
|
||||
return True
|
||||
|
||||
|
@ -105,7 +112,7 @@ class TaskPanel:
|
|||
# Get objects
|
||||
selObjs = Geometry.getSelectedObjs()
|
||||
if not selObjs:
|
||||
msg = Translator.translate("Ship instance must be selected (any object selected)\n")
|
||||
msg = Translator.translate("Ship instance must be selected (no object selected)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
for i in range(0,len(selObjs)):
|
||||
|
@ -125,7 +132,7 @@ class TaskPanel:
|
|||
self.ship = obj
|
||||
# Test if any valid ship was selected
|
||||
if not self.ship:
|
||||
msg = Translator.translate("Ship instance must be selected (any valid ship found at selected objects)\n")
|
||||
msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
# Get bounds
|
||||
|
@ -208,12 +215,12 @@ class TaskPanel:
|
|||
props.index("AreaCurveDraft")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyFloat","AreaCurveDraft","Ship", str(Translator.translate("Areas curve draft selected [m]")))
|
||||
self.ship.AreaCurveDraft = self.form.draft.value()
|
||||
self.ship.AreaCurveDraft = self.form.draft.value()
|
||||
try:
|
||||
props.index("AreaCurveTrim")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyFloat","AreaCurveTrim","Ship", str(Translator.translate("Areas curve trim selected [m]")))
|
||||
self.ship.AreaCurveTrim = self.form.draft.value()
|
||||
self.ship.AreaCurveTrim = self.form.trim.value()
|
||||
|
||||
def createTask():
|
||||
panel = TaskPanel()
|
||||
|
|
|
@ -119,7 +119,9 @@ class TaskPanel:
|
|||
self.faces = None
|
||||
selObjs = Geometry.getSelectedObjs()
|
||||
if not selObjs:
|
||||
msg = Translator.translate("All ship surfaces must be selected (Any object has been selected)\n")
|
||||
msg = Translator.translate("Ship objects can only be created on top of hull geometry (no object selected).\n")
|
||||
App.Console.PrintError(msg)
|
||||
msg = Translator.translate("Please create or download a ship hull geometry before using this tool\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
self.faces = []
|
||||
|
@ -128,31 +130,48 @@ class TaskPanel:
|
|||
for j in range(0, len(faces)):
|
||||
self.faces.append(faces[j])
|
||||
if not self.faces:
|
||||
msg = Translator.translate("All ship surfaces must be selected (Any face found into selected objects)\n")
|
||||
msg = Translator.translate("Ship objects can only be created on top of hull geometry (no face object selected).\n")
|
||||
App.Console.PrintError(msg)
|
||||
msg = Translator.translate("Please create or download a ship hull geometry before using this tool\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
# Get bounds
|
||||
bounds = [0.0, 0.0, 0.0]
|
||||
bbox = self.faces[0].BoundBox
|
||||
bounds[0] = bbox.XLength
|
||||
bounds[1] = bbox.YLength
|
||||
bounds[2] = bbox.ZLength
|
||||
minX = bbox.XMin
|
||||
maxX = bbox.XMax
|
||||
minY = bbox.YMin
|
||||
maxY = bbox.YMax
|
||||
minZ = bbox.ZMin
|
||||
maxZ = bbox.ZMax
|
||||
for i in range(1,len(self.faces)):
|
||||
bbox = self.faces[i].BoundBox
|
||||
if bounds[0] < bbox.XLength:
|
||||
bounds[0] = bbox.XLength
|
||||
if bounds[1] < bbox.YLength:
|
||||
bounds[1] = bbox.YLength
|
||||
if bounds[2] < bbox.ZLength:
|
||||
bounds[2] = bbox.ZLength
|
||||
if minX > bbox.XMin:
|
||||
minX = bbox.XMin
|
||||
if maxX < bbox.XMax:
|
||||
maxX = bbox.XMax
|
||||
if minY > bbox.YMin:
|
||||
minY = bbox.YMin
|
||||
if maxY < bbox.YMax:
|
||||
maxY = bbox.YMax
|
||||
if minZ > bbox.ZMin:
|
||||
minZ = bbox.ZMin
|
||||
if maxZ < bbox.ZMax:
|
||||
maxZ = bbox.ZMax
|
||||
bounds[0] = maxX - minX
|
||||
bounds[1] = max(maxY - minY, abs(maxY), abs(minY))
|
||||
bounds[2] = maxZ - minZ
|
||||
# Set UI fields
|
||||
self.form.length.setMaximum(bounds[0])
|
||||
self.form.length.setMinimum(0.001)
|
||||
self.form.length.setValue(bounds[0])
|
||||
self.L = bounds[0]
|
||||
self.form.beam.setMaximum(2.0*bounds[1])
|
||||
self.form.beam.setMinimum(0.001)
|
||||
self.form.beam.setValue(2.0*bounds[1])
|
||||
self.B = 2.0*bounds[1]
|
||||
self.form.draft.setMaximum(bounds[2])
|
||||
self.form.draft.setMinimum(0.001)
|
||||
self.form.draft.setValue(0.5*bounds[2])
|
||||
self.T = 0.5*bounds[2]
|
||||
msg = Translator.translate("Ready to work\n")
|
||||
|
|
252
src/Mod/Ship/shipHydrostatics/Plot.py
Normal file
252
src/Mod/Ship/shipHydrostatics/Plot.py
Normal file
|
@ -0,0 +1,252 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2011, 2012 *
|
||||
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import os
|
||||
# FreeCAD modules
|
||||
import FreeCAD,FreeCADGui
|
||||
from FreeCAD import Part, Base
|
||||
from FreeCAD import Image, ImageGui
|
||||
# FreeCADShip modules
|
||||
from shipUtils import Paths, Translator
|
||||
import Tools
|
||||
|
||||
header = """ #################################################################
|
||||
|
||||
##### #### ### #### ##### # # ### ####
|
||||
# # # # # # # # # # # #
|
||||
# ## #### #### # # # # # # # # # # #
|
||||
#### # # # # # # # ##### # # ## ## ##### # ####
|
||||
# # #### #### # # # # # # # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # #### #### ### # # #### ##### # # ### #
|
||||
|
||||
#################################################################
|
||||
"""
|
||||
|
||||
class Plot(object):
|
||||
def __init__(self, ship, trim, drafts):
|
||||
""" Constructor. performs plot and show it (Using pyxplot).
|
||||
@param ship Selected ship instance
|
||||
@param trim Trim in degrees.
|
||||
@param drafts List of drafts to be performed.
|
||||
"""
|
||||
if self.createDirectory():
|
||||
return
|
||||
if self.saveData(ship, trim, drafts):
|
||||
return
|
||||
if self.saveLayout(trim):
|
||||
return
|
||||
if self.execute():
|
||||
return
|
||||
ImageGui.open(self.path + 'volume.png')
|
||||
ImageGui.open(self.path + 'stability.png')
|
||||
ImageGui.open(self.path + 'coeffs.png')
|
||||
|
||||
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 = Translator.translate("Can't create '" + self.path + "' folder.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return False
|
||||
|
||||
def saveData(self, ship, trim, drafts):
|
||||
""" Write data file.
|
||||
@param ship Selected ship instance
|
||||
@param trim Trim in degrees.
|
||||
@param drafts List of drafts to be performed.
|
||||
@return True if error happens.
|
||||
"""
|
||||
# Open the file
|
||||
filename = self.path + 'hydrostatics.dat'
|
||||
try:
|
||||
Output = open(filename, "w")
|
||||
except IOError:
|
||||
msg = Translator.translate("Can't write '" + filename + "' file.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
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(drafts)):
|
||||
draft = drafts[i]
|
||||
point = Tools.Point(ship,draft,trim)
|
||||
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 = Translator.translate("Data saved at '" + self.dataFile + "'.\n")
|
||||
FreeCAD.Console.PrintMessage(msg)
|
||||
return False
|
||||
|
||||
def saveLayout(self, trim):
|
||||
""" Prints the pyxplot layout.
|
||||
@param trim Trim in degrees.
|
||||
@return True if error happens.
|
||||
"""
|
||||
filename = self.path + 'volume.pyxplot'
|
||||
# Open the file
|
||||
try:
|
||||
Output = open(filename, "w")
|
||||
except IOError:
|
||||
msg = Translator.translate("Can't write '" + filename + "' file.\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
# Write header
|
||||
Output.write(header)
|
||||
Output.write(" #\n")
|
||||
Output.write(" # File automatically exported by FreeCAD-Ship\n")
|
||||
Output.write(" # This file contains a script to plot transversal areas curve.\n")
|
||||
Output.write(" # To use it execute:\n")
|
||||
Output.write(" #\n")
|
||||
Output.write(" # pyxplot %s\n" % (filename))
|
||||
Output.write(" #\n")
|
||||
Output.write(" #################################################################\n")
|
||||
# Write general options for hydrostatics
|
||||
Output.write("set numeric display latex\n")
|
||||
Output.write("set output '%s'\n" % (self.path + 'volume.eps'))
|
||||
Output.write("set title '$trim$ = %g [degrees]'\n" % (trim))
|
||||
Output.write("set key below\n")
|
||||
Output.write("set grid\n")
|
||||
# Configure axis
|
||||
Output.write("# Y axis\n")
|
||||
Output.write("set ylabel '$\\bigtriangleup$ / $\\mathrm{ton}$'\n")
|
||||
Output.write("set ytic\n")
|
||||
Output.write("# X axis\n")
|
||||
Output.write("set xlabel '$Draft$ / $\\mathrm{m}$'\n")
|
||||
Output.write("set xtic\n")
|
||||
Output.write("set x2label '\\textit{Wetted area} / $\\mathrm{m}^2$'\n")
|
||||
Output.write("set x2tic\n")
|
||||
Output.write("set x3label '\\textit{1cm trim moment} / $\\mathrm{ton} \\times \\mathrm{m}$'\n")
|
||||
Output.write("set x3tic\n")
|
||||
Output.write("set x4label '$XCB$ / $\\mathrm{m}$'\n")
|
||||
Output.write("set x4tic\n")
|
||||
Output.write("set axis x2 top\n")
|
||||
Output.write("set axis x4 top\n")
|
||||
Output.write("# Line styles\n")
|
||||
Output.write("set style 1 line linetype 1 linewidth 1 colour rgb (0):(0):(0)\n")
|
||||
Output.write("set style 2 line linetype 1 linewidth 1 colour rgb (1):(0):(0)\n")
|
||||
Output.write("set style 3 line linetype 1 linewidth 1 colour rgb (0):(0):(1)\n")
|
||||
Output.write("set style 4 line linetype 1 linewidth 1 colour rgb (0.1):(0.5):(0.1)\n")
|
||||
# Write plot call
|
||||
Output.write("# Plot\n")
|
||||
Output.write("plot '%s' using 2:1 title 'Draft' axes x1y1 with lines style 1, \\\n" % (self.dataFile))
|
||||
Output.write(" '' using 3:1 title 'Wetted area' axes x2y1 with lines style 2, \\\n")
|
||||
Output.write(" '' using 4:1 title '1cm trim moment' axes x3y1 with lines style 3, \\\n")
|
||||
Output.write(" '' using 5:1 title 'XCB' axes x4y1 with lines style 4\n")
|
||||
# Prepare second plot
|
||||
Output.write("set output '%s'\n" % (self.path + 'stability.eps'))
|
||||
Output.write("# X axis\n")
|
||||
Output.write("set x2label '\\textit{Floating area} / $\\mathrm{m}^2$'\n")
|
||||
Output.write("set x2tic\n")
|
||||
Output.write("set x3label '$KB_{T}$ / $\\mathrm{m}$'\n")
|
||||
Output.write("set x3tic\n")
|
||||
Output.write("set x4label '$BM_{T}$ / $\\mathrm{m}$'\n")
|
||||
Output.write("set x4tic\n")
|
||||
# Write plot call
|
||||
Output.write("# Plot\n")
|
||||
Output.write("plot '%s' using 2:1 title 'Draft' axes x1y1 with lines style 1, \\\n" % (self.dataFile))
|
||||
Output.write(" '' using 6:1 title 'Floating area' axes x2y1 with lines style 2, \\\n")
|
||||
Output.write(" '' using 7:1 title '$KB_{T}$' axes x3y1 with lines style 3, \\\n")
|
||||
Output.write(" '' using 8:1 title '$BM_{T}$' axes x4y1 with lines style 4\n")
|
||||
# Prepare third plot
|
||||
Output.write("set output '%s'\n" % (self.path + 'coeffs.eps'))
|
||||
Output.write("# X axis\n")
|
||||
Output.write("set x2label '$C_{B}$'\n")
|
||||
Output.write("set x2tic\n")
|
||||
Output.write("set x3label '$C_{F}$'\n")
|
||||
Output.write("set x3tic\n")
|
||||
Output.write("set x4label '$C_{M}$'\n")
|
||||
Output.write("set x4tic\n")
|
||||
# Write plot call
|
||||
Output.write("# Plot\n")
|
||||
Output.write("plot '%s' using 2:1 title 'Draft' axes x1y1 with lines style 1, \\\n" % (self.dataFile))
|
||||
Output.write(" '' using 9:1 title '$C_{B}$' axes x2y1 with lines style 2, \\\n")
|
||||
Output.write(" '' using 10:1 title '$C_{F}$' axes x3y1 with lines style 3, \\\n")
|
||||
Output.write(" '' using 11:1 title '$C_{M}$' axes x4y1 with lines style 4\n")
|
||||
# Close file
|
||||
self.layoutFile = filename
|
||||
Output.close()
|
||||
return False
|
||||
|
||||
def execute(self):
|
||||
""" Calls pyxplot in order to plot an save an image.
|
||||
@return True if error happens.
|
||||
"""
|
||||
# Plot
|
||||
filename = self.path + 'volume'
|
||||
comm = "pyxplot %s" % (self.layoutFile)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute pyxplot. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Plot will not generated\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
# Convert volume image
|
||||
comm = "gs -r300 -dEPSCrop -dTextAlphaBits=4 -sDEVICE=png16m -sOutputFile=%s.png -dBATCH -dNOPAUSE %s.eps" % (filename,filename)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute ghostscript. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Generated image will not converted to png\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
# Convert stability image
|
||||
filename = self.path + 'stability'
|
||||
comm = "gs -r300 -dEPSCrop -dTextAlphaBits=4 -sDEVICE=png16m -sOutputFile=%s.png -dBATCH -dNOPAUSE %s.eps" % (filename,filename)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute ghostscript. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Generated image will not converted to png\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
# Convert coefficients image
|
||||
filename = self.path + 'coeffs'
|
||||
comm = "gs -r300 -dEPSCrop -dTextAlphaBits=4 -sDEVICE=png16m -sOutputFile=%s.png -dBATCH -dNOPAUSE %s.eps" % (filename,filename)
|
||||
if os.system(comm):
|
||||
msg = Translator.translate("Can't execute ghostscript. Maybe is not installed?\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
msg = Translator.translate("Generated image will not converted to png\n")
|
||||
FreeCAD.Console.PrintError(msg)
|
||||
return True
|
||||
return False
|
230
src/Mod/Ship/shipHydrostatics/TaskPanel.py
Normal file
230
src/Mod/Ship/shipHydrostatics/TaskPanel.py
Normal file
|
@ -0,0 +1,230 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2011, 2012 *
|
||||
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import math
|
||||
# FreeCAD modules
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
# Qt library
|
||||
from PyQt4 import QtGui,QtCore
|
||||
# Module
|
||||
import Plot
|
||||
import Instance
|
||||
from shipUtils import Paths, Translator
|
||||
from surfUtils import Geometry
|
||||
import Tools
|
||||
|
||||
class TaskPanel:
|
||||
def __init__(self):
|
||||
self.ui = Paths.modulePath() + "/shipHydrostatics/TaskPanel.ui"
|
||||
self.ship = None
|
||||
|
||||
def accept(self):
|
||||
if not self.ship:
|
||||
return False
|
||||
self.save()
|
||||
draft = self.form.minDraft.value()
|
||||
drafts = [draft]
|
||||
dDraft = (self.form.maxDraft.value() - self.form.minDraft.value())/self.form.nDraft.value()
|
||||
for i in range(1,self.form.nDraft.value()):
|
||||
draft = draft + dDraft
|
||||
drafts.append(draft)
|
||||
Plot.Plot(self.ship, self.form.trim.value(), drafts)
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
return True
|
||||
|
||||
def clicked(self, index):
|
||||
pass
|
||||
|
||||
def open(self):
|
||||
pass
|
||||
|
||||
def needsFullSpace(self):
|
||||
return True
|
||||
|
||||
def isAllowedAlterSelection(self):
|
||||
return False
|
||||
|
||||
def isAllowedAlterView(self):
|
||||
return True
|
||||
|
||||
def isAllowedAlterDocument(self):
|
||||
return False
|
||||
|
||||
def helpRequested(self):
|
||||
pass
|
||||
|
||||
def setupUi(self):
|
||||
mw = self.getMainWindow()
|
||||
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
||||
form.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 getMainWindow(self):
|
||||
"returns the main window"
|
||||
# using QtGui.qApp.activeWindow() isn't very reliable because if another
|
||||
# widget than the mainwindow is active (e.g. a dialog) the wrong widget is
|
||||
# returned
|
||||
toplevel = QtGui.qApp.topLevelWidgets()
|
||||
for i in toplevel:
|
||||
if i.metaObject().className() == "Gui::MainWindow":
|
||||
return i
|
||||
raise Exception("No main window found")
|
||||
|
||||
def initValues(self):
|
||||
""" Set initial values for fields
|
||||
"""
|
||||
# Get objects
|
||||
selObjs = Geometry.getSelectedObjs()
|
||||
if not selObjs:
|
||||
msg = Translator.translate("Ship instance must be selected (no object selected)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
for i in range(0,len(selObjs)):
|
||||
obj = selObjs[i]
|
||||
# Test if is a ship instance
|
||||
props = obj.PropertiesList
|
||||
try:
|
||||
props.index("IsShip")
|
||||
except ValueError:
|
||||
continue
|
||||
if obj.IsShip:
|
||||
# Test if another ship already selected
|
||||
if self.ship:
|
||||
msg = Translator.translate("More than one ship selected (extra ship will be neglected)\n")
|
||||
App.Console.PrintWarning(msg)
|
||||
break
|
||||
self.ship = obj
|
||||
# Test if any valid ship was selected
|
||||
if not self.ship:
|
||||
msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
# 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
|
||||
msg = Translator.translate("Ready to work\n")
|
||||
App.Console.PrintMessage(msg)
|
||||
return False
|
||||
|
||||
def retranslateUi(self):
|
||||
""" Set user interface locale strings.
|
||||
"""
|
||||
self.form.setWindowTitle(Translator.translate("Plot hydrostatics"))
|
||||
self.form.findChild(QtGui.QLabel, "TrimLabel").setText(Translator.translate("Trim"))
|
||||
self.form.findChild(QtGui.QLabel, "MinDraftLabel").setText(Translator.translate("Minimum draft"))
|
||||
self.form.findChild(QtGui.QLabel, "MaxDraftLabel").setText(Translator.translate("Maximum draft"))
|
||||
self.form.findChild(QtGui.QLabel, "NDraftLabel").setText(Translator.translate("Number of points"))
|
||||
|
||||
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 save(self):
|
||||
""" Saves data into ship instance.
|
||||
"""
|
||||
props = self.ship.PropertiesList
|
||||
try:
|
||||
props.index("HydrostaticsTrim")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyFloat","HydrostaticsTrim","Ship", str(Translator.translate("Hydrostatics trim selected [m]")))
|
||||
self.ship.HydrostaticsTrim = self.form.trim.value()
|
||||
try:
|
||||
props.index("HydrostaticsMinDraft")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyFloat","HydrostaticsMinDraft","Ship", str(Translator.translate("Hydrostatics minimum draft selected [m]")))
|
||||
self.ship.HydrostaticsMinDraft = self.form.minDraft.value()
|
||||
try:
|
||||
props.index("HydrostaticsMaxDraft")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyFloat","HydrostaticsMaxDraft","Ship", str(Translator.translate("Hydrostatics maximum draft selected [m]")))
|
||||
self.ship.HydrostaticsMaxDraft = self.form.maxDraft.value()
|
||||
try:
|
||||
props.index("HydrostaticsNDraft")
|
||||
except ValueError:
|
||||
self.ship.addProperty("App::PropertyInteger","HydrostaticsNDraft","Ship", str(Translator.translate("Hydrostatics number of points selected [m]")))
|
||||
self.ship.HydrostaticsNDraft = self.form.nDraft.value()
|
||||
|
||||
def createTask():
|
||||
panel = TaskPanel()
|
||||
Gui.Control.showDialog(panel)
|
||||
if panel.setupUi():
|
||||
Gui.Control.closeDialog(panel)
|
||||
return None
|
||||
return panel
|
205
src/Mod/Ship/shipHydrostatics/TaskPanel.ui
Normal file
205
src/Mod/Ship/shipHydrostatics/TaskPanel.ui
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPanel</class>
|
||||
<widget class="QWidget" name="TaskPanel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>260</width>
|
||||
<height>256</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create new ship</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="TrimLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Trim</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="Trim">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-45.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>45.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="TrimUnits">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Deg</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="DraftBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Drafts</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="gridLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>9</x>
|
||||
<y>19</y>
|
||||
<width>231</width>
|
||||
<height>181</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="MinDraftLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimum draft</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="MinDraft">
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="MaxDraftLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Maximum draft</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="MaxDraft">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="NDraftLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Number of points</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="MinDraftDim">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>m</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="MaxDraftDim">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>m</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="NDraft">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>11</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -27,25 +27,112 @@ import FreeCAD as App
|
|||
import FreeCADGui as Gui
|
||||
# Module
|
||||
import Instance
|
||||
from shipUtils import Math
|
||||
|
||||
def convertSection(section, x, z):
|
||||
""" Transform linear points distribution of full section
|
||||
into double list, where points are gropued by height, and
|
||||
reachs z maximum height.
|
||||
@param section Ship section.
|
||||
@param x Ship section x coordinate.
|
||||
@param z Maximum section height.
|
||||
@return Converted section, None if no valid section (i.e.- All section are over z)
|
||||
"""
|
||||
# Convert into array with n elements (Number of points by sections)
|
||||
# with m elements into them (Number of points with the same height,
|
||||
# that is typical of multibody)
|
||||
points = []
|
||||
nPoints = 0
|
||||
j = 0
|
||||
while j < len(section)-1:
|
||||
points.append([section[j]])
|
||||
k = j+1
|
||||
last=False # In order to identify if last point has been append
|
||||
while(k < len(section)):
|
||||
if not Math.isAprox(section[j].z, section[k].z):
|
||||
break
|
||||
points[nPoints].append(section[k])
|
||||
last=True
|
||||
k = k+1
|
||||
nPoints = nPoints + 1
|
||||
j = k
|
||||
if not last: # Last point has not been added
|
||||
points.append([section[len(section)-1]])
|
||||
# Count the number of valid points
|
||||
n = 0
|
||||
for j in range(0,len(points)):
|
||||
Z = points[j][0].z
|
||||
if Z > z:
|
||||
break
|
||||
n = n+1
|
||||
# Discard invalid sections
|
||||
if n == 0:
|
||||
return None
|
||||
# Truncate only valid points
|
||||
l = points[0:n]
|
||||
# Study if additional point is needed
|
||||
if n < len(points):
|
||||
# Get last sections
|
||||
pts1 = points[n]
|
||||
pts0 = points[n-1]
|
||||
if len(pts1) == len(pts0):
|
||||
# Simple interpolation between points
|
||||
# \----\-----|--------|
|
||||
# \ | | /
|
||||
# \ \ | |
|
||||
# \----|--|-----/
|
||||
pts = []
|
||||
for i in range(0,len(pts1)):
|
||||
y0 = pts0[i].y
|
||||
z0 = pts0[i].z
|
||||
y1 = pts1[i].y
|
||||
z1 = pts1[i].z
|
||||
factor = (z - z0) / (z1 - z0)
|
||||
y = y0 + factor*(y1 - y0)
|
||||
pts.append(App.Base.Vector(x,y,z))
|
||||
l.append(pts)
|
||||
if len(pts1) > len(pts0):
|
||||
# pts0 has been multiplied
|
||||
# \---|---|
|
||||
# \ | |
|
||||
# \ | |
|
||||
# \|---|
|
||||
# @todo Only katamaran are involved, multiple points multiplication must be study
|
||||
pts = []
|
||||
for i in range(0,len(pts1)):
|
||||
y0 = pts0[min(len(pts0)-1,i)].y
|
||||
z0 = pts0[min(len(pts0)-1,i)].z
|
||||
y1 = pts1[i].y
|
||||
z1 = pts1[i].z
|
||||
factor = (z - z0) / (z1 - z0)
|
||||
y = y0 + factor*(y1 - y0)
|
||||
pts.append(App.Base.Vector(x,y,z))
|
||||
l.append(pts)
|
||||
# @todo Only katamaran are involved, multiple points creation/destruction must be study
|
||||
return l
|
||||
|
||||
def Displacement(ship, draft, trim):
|
||||
""" Calculate ship displacement.
|
||||
@param ship Selected ship instance
|
||||
@param traft Draft.
|
||||
@param draft Draft.
|
||||
@param trim Trim in degrees.
|
||||
@return [areas,disp,xcb]: \n
|
||||
areas : Area of each section \n
|
||||
disp: Ship displacement \n
|
||||
xcb: X bouyance center coordinate
|
||||
Cb: Block coefficient
|
||||
"""
|
||||
angle = math.radians(trim)
|
||||
sections = Instance.sections(ship)
|
||||
xCoord = ship.xSection[:]
|
||||
minX = None
|
||||
maxX = None
|
||||
maxY = 0.0
|
||||
areas = []
|
||||
vol = 0.0
|
||||
moment = 0.0
|
||||
if not sections:
|
||||
return [[],0.0,0.0]
|
||||
return [[],0.0,0.0,0.0]
|
||||
for i in range(0, len(sections)):
|
||||
# Get the section
|
||||
section = sections[i]
|
||||
|
@ -56,39 +143,85 @@ def Displacement(ship, draft, trim):
|
|||
x = xCoord[i]
|
||||
# Get the maximum Z value
|
||||
Z = draft - x*math.tan(angle)
|
||||
# Count the number of valid points
|
||||
n = 0
|
||||
for j in range(0,len(section)):
|
||||
z = section[j].z
|
||||
if z > Z:
|
||||
break
|
||||
n = n+1
|
||||
# Discard invalid sections
|
||||
if n == 0:
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
areas.append(0.0)
|
||||
continue
|
||||
# Truncate only valid points
|
||||
points = section[0:n]
|
||||
# Study if additional point is needed
|
||||
if n < len(section):
|
||||
y0 = section[n-1].y
|
||||
z0 = section[n-1].z
|
||||
y1 = section[n].y
|
||||
z1 = section[n].z
|
||||
factor = (Z - z0) / (z1 - z0)
|
||||
y = y0 + factor*(y1 - y0)
|
||||
points.append(App.Base.Vector(x,y,Z))
|
||||
maxX = x
|
||||
if not minX:
|
||||
minX = x
|
||||
# Integrate area
|
||||
area = 0.0
|
||||
for j in range(0, len(points)-1):
|
||||
y0 = abs(points[j].y)
|
||||
z0 = points[j].z
|
||||
y1 = abs(points[j+1].y)
|
||||
z1 = points[j+1].z
|
||||
y = 0.5 * (y0 + y1)
|
||||
dz = z1 - z0
|
||||
area = area + 2.0*y*dz # 2x because only half ship is represented
|
||||
areas.append(area)
|
||||
for j in range(0, len(section)-1):
|
||||
for k in range(0, min(len(section[j])-1, len(section[j+1])-1)):
|
||||
# y11,z11 ------- y01,z01
|
||||
# | |
|
||||
# | |
|
||||
# | |
|
||||
# y10,z10 ------- y00,z00
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = 0.5*((y00 - y10) + (y01 - y11))
|
||||
dz = 0.5*((z01 - z00) + (z11 - z10))
|
||||
area = area + dy*dz
|
||||
maxY = max([maxY,y00,y10,y01,y11])
|
||||
if(len(section[j]) < len(section[j+1])):
|
||||
# y01,z01 ------- y11,z11
|
||||
# | __/
|
||||
# | __/
|
||||
# | /
|
||||
# y00,z00
|
||||
k = len(section[j])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y01 - y11
|
||||
dz = z01 - z00
|
||||
area = area + 0.5*dy*dz
|
||||
maxY = max([maxY,y00,y01,y11])
|
||||
elif(len(section[j]) > len(section[j+1])):
|
||||
# y01,z01
|
||||
# | \__
|
||||
# | \__
|
||||
# | \
|
||||
# y00,z00 ------- y10,z10
|
||||
k = len(section[j+1])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
dy = y00 - y10
|
||||
dz = z01 - z00
|
||||
area = area + 0.5*dy*dz
|
||||
maxY = max([maxY,y00,y10,y01])
|
||||
elif(len(section[j]) == 1):
|
||||
# y1,z1 -------
|
||||
# |
|
||||
# |
|
||||
# |
|
||||
# y0,z0 -------
|
||||
k = 0
|
||||
y0 = abs(section[j][k].y)
|
||||
z0 = section[j][k].z
|
||||
y1 = abs(section[j+1][k].y)
|
||||
z1 = section[j+1][k].z
|
||||
dy = 0.5 * (y0 + y1)
|
||||
dz = z1 - z0
|
||||
area = area + dy*dz
|
||||
maxY = max([maxY,y0,y1])
|
||||
areas.append(2.0*area) # 2x because only half ship is represented
|
||||
# Add volume & moment if proceed
|
||||
if i > 0:
|
||||
dx = xCoord[i] - xCoord[i-1]
|
||||
|
@ -97,8 +230,647 @@ def Displacement(ship, draft, trim):
|
|||
vol = vol + area*dx
|
||||
moment = moment + area*dx*x
|
||||
# Compute displacement and xcb
|
||||
disp = vol / 1.025 # rho = 1.025 ton/m3 (salt water density)
|
||||
xcb = 0.0
|
||||
disp = vol / 1.025 # rho = 1.025 ton/m3 (salt water density)
|
||||
xcb = 0.0
|
||||
cb = 0.0
|
||||
if vol > 0.0:
|
||||
xcb = moment / vol
|
||||
return [areas,disp,xcb]
|
||||
block = (maxX-minX)*2.0*maxY*draft
|
||||
cb = vol / block
|
||||
return [areas,disp,xcb,cb]
|
||||
|
||||
def WettedArea(ship, draft, trim):
|
||||
""" Calculate wetted ship area.
|
||||
@param ship Selected ship instance
|
||||
@param draft Draft.
|
||||
@param trim Trim in degrees.
|
||||
@return Wetted ship area.
|
||||
"""
|
||||
angle = math.radians(trim)
|
||||
sections = Instance.sections(ship)
|
||||
xCoord = ship.xSection[:]
|
||||
lines = []
|
||||
area = 0.0
|
||||
if not sections:
|
||||
return 0.0
|
||||
for i in range(0, len(sections)):
|
||||
# Get the section
|
||||
section = sections[i]
|
||||
if len(section) < 2: # Empty section
|
||||
lines.append(0.0)
|
||||
continue
|
||||
# Get the position of the section
|
||||
x = xCoord[i]
|
||||
# Get the maximum Z value
|
||||
Z = draft - x*math.tan(angle)
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
lines.append(0.0)
|
||||
continue
|
||||
# Integrate line area
|
||||
line = 0.0
|
||||
for j in range(0, len(section)-1):
|
||||
for k in range(0, min(len(section[j])-1, len(section[j+1])-1)):
|
||||
# y11,z11 ------- y01,z01
|
||||
# | |
|
||||
# | |
|
||||
# | |
|
||||
# y10,z10 ------- y00,z00
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y11 - y10
|
||||
dz = z11 - z10
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
dy = y01 - y00
|
||||
dz = z01 - z00
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
if(len(section[j]) < len(section[j+1])):
|
||||
# y01,z01 ------- y11,z11
|
||||
# | __/
|
||||
# | __/
|
||||
# | /
|
||||
# y00,z00
|
||||
k = len(section[j])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y11 - y00
|
||||
dz = z11 - z00
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
dy = y01 - y00
|
||||
dz = z01 - z00
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
elif(len(section[j]) > len(section[j+1])):
|
||||
# y01,z01
|
||||
# | \__
|
||||
# | \__
|
||||
# | \
|
||||
# y00,z00 ------- y10,z10
|
||||
k = len(section[j+1])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
dy = y01 - y00
|
||||
dz = z01 - z00
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
dy = y01 - y10
|
||||
dz = z01 - z10
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
elif(len(section[j]) == 1):
|
||||
# y1,z1 -------
|
||||
# |
|
||||
# |
|
||||
# |
|
||||
# y0,z0 -------
|
||||
k = 0
|
||||
y0 = abs(section[j][k].y)
|
||||
z0 = section[j][k].z
|
||||
y1 = abs(section[j+1][k].y)
|
||||
z1 = section[j+1][k].z
|
||||
dy = y1 - y0
|
||||
dz = z1 - z0
|
||||
line = line + math.sqrt(dy*dy + dz*dz)
|
||||
lines.append(2.0*line) # 2x because only half ship is represented
|
||||
# Add area if proceed
|
||||
if i > 0:
|
||||
dx = xCoord[i] - xCoord[i-1]
|
||||
x = 0.5*(xCoord[i] + xCoord[i-1])
|
||||
line = 0.5*(lines[i] + lines[i-1])
|
||||
area = area + line*dx
|
||||
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.
|
||||
"""
|
||||
angle = math.degrees(math.atan2(0.01,0.5*ship.Length))
|
||||
newTrim = trim + angle
|
||||
data = Displacement(ship,draft,newTrim)
|
||||
mom0 = -disp*xcb
|
||||
mom1 = -data[1]*data[2]
|
||||
return mom1 - mom0
|
||||
|
||||
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.
|
||||
"""
|
||||
angle = math.radians(trim)
|
||||
sections = Instance.sections(ship)
|
||||
xCoord = ship.xSection[:]
|
||||
lines = []
|
||||
area = 0.0
|
||||
minX = None
|
||||
maxX = None
|
||||
maxY = 0.0
|
||||
cf = 0.0
|
||||
if not sections:
|
||||
return [0.0, 0.0]
|
||||
for i in range(0, len(sections)):
|
||||
# Get the section
|
||||
section = sections[i]
|
||||
if len(section) < 2: # Empty section
|
||||
lines.append(0.0)
|
||||
continue
|
||||
# Get the position of the section
|
||||
x = xCoord[i]
|
||||
# Get the maximum Z value
|
||||
Z = draft - x*math.tan(angle)
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
lines.append(0.0)
|
||||
continue
|
||||
maxX = x
|
||||
if not minX:
|
||||
minX = x
|
||||
# Get floating line length
|
||||
line = 0.0
|
||||
flag = True # Even lines compute for floating areas, odd no
|
||||
j = len(section)-1
|
||||
k = len(section[j])-1
|
||||
while k>0:
|
||||
k = k-1
|
||||
if flag:
|
||||
y0 = abs(section[j][k-1].y)
|
||||
y1 = abs(section[j][k].y)
|
||||
line = line + (y1 - y0)
|
||||
maxY = max([maxY,y1,y0])
|
||||
flag = not flag
|
||||
if flag: # Central body computation lefts
|
||||
y = abs(section[j][0].y)
|
||||
line = line + y
|
||||
maxY = max([maxY,y])
|
||||
lines.append(2.0*line) # 2x because only half ship is represented
|
||||
# Add area if proceed
|
||||
if i > 0:
|
||||
dx = xCoord[i] - xCoord[i-1]
|
||||
x = 0.5*(xCoord[i] + xCoord[i-1])
|
||||
line = 0.5*(lines[i] + lines[i-1])
|
||||
area = area + line*dx
|
||||
if area:
|
||||
cf = area / ( (maxX-minX) * 2.0*maxY )
|
||||
return [area, cf]
|
||||
|
||||
def KBT(ship, draft, trim, roll=0.0):
|
||||
""" Calculate ship Keel to Bouyance center transversal distance.
|
||||
@param ship Selected ship instance
|
||||
@param draft Draft.
|
||||
@param trim Trim in degrees.
|
||||
@param roll Roll angle in degrees.
|
||||
@return [KBTx, KBTy]: \n
|
||||
KBTy : TRansversal KB y coordinate \n
|
||||
KBTz : TRansversal KB z coordinate
|
||||
"""
|
||||
angle = math.radians(trim)
|
||||
rAngle = math.radians(roll)
|
||||
sections = Instance.sections(ship)
|
||||
xCoord = ship.xSection[:]
|
||||
areas = []
|
||||
vol = 0.0
|
||||
vMy = 0.0
|
||||
vMz = 0.0
|
||||
My = []
|
||||
Mz = []
|
||||
kb = [0.0,0.0]
|
||||
if not sections:
|
||||
return [[],0.0,0.0]
|
||||
for i in range(0, len(sections)):
|
||||
# ------------------------------------
|
||||
# Board
|
||||
# ------------------------------------
|
||||
# Get the section
|
||||
section = sections[i]
|
||||
if len(section) < 2: # Empty section
|
||||
areas.append(0.0)
|
||||
continue
|
||||
# Get the position of the section
|
||||
x = xCoord[i]
|
||||
# Get the maximum Z value
|
||||
Z = draft - x*math.tan(angle)
|
||||
# Format section
|
||||
aux = convertSection(section, x, Z)
|
||||
if not aux:
|
||||
areas.append(0.0)
|
||||
My.append(0.0)
|
||||
Mz.append(0.0)
|
||||
continue
|
||||
# Correct by roll angle
|
||||
pts = aux[len(aux)-1]
|
||||
beam = pts[0].y
|
||||
for i in range(1,len(pts)):
|
||||
beam = max(beam, pts[i].y)
|
||||
Z = Z - beam*math.tan(rAngle)
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
areas.append(0.0)
|
||||
My.append(0.0)
|
||||
Mz.append(0.0)
|
||||
continue
|
||||
# Integrate area
|
||||
area = 0.0
|
||||
momy = 0.0
|
||||
momz = 0.0
|
||||
for j in range(0, len(section)-1):
|
||||
for k in range(0, min(len(section[j])-1, len(section[j+1])-1)):
|
||||
# y11,z11 ------- y01,z01
|
||||
# | |
|
||||
# | |
|
||||
# | |
|
||||
# y10,z10 ------- y00,z00
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = 0.5*((y00 - y10) + (y01 - y11))
|
||||
dz = 0.5*((z01 - z00) + (z11 - z10))
|
||||
y = 0.25*(y00 + y10 + y01 + y11)
|
||||
z = 0.25*(z01 + z00 + z11 + z10)
|
||||
area = area + dy*dz
|
||||
momy = momy + y*dy*dz
|
||||
momz = momz + z*dy*dz
|
||||
if(len(section[j]) < len(section[j+1])):
|
||||
# y01,z01 ------- y11,z11
|
||||
# | __/
|
||||
# | __/
|
||||
# | /
|
||||
# y00,z00
|
||||
k = len(section[j])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y01 - y11
|
||||
dz = z01 - z00
|
||||
y = 0.33*(y00 + y01 + y11)
|
||||
z = 0.33*(z01 + z00 + z11)
|
||||
area = area + 0.5*dy*dz
|
||||
momy = momy + y*0.5*dy*dz
|
||||
momz = momz + z*0.5*dy*dz
|
||||
elif(len(section[j]) > len(section[j+1])):
|
||||
# y01,z01
|
||||
# | \__
|
||||
# | \__
|
||||
# | \
|
||||
# y00,z00 ------- y10,z10
|
||||
k = len(section[j+1])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
dy = y00 - y10
|
||||
dz = z01 - z00
|
||||
y = 0.33*(y00 + y01 + y10)
|
||||
z = 0.33*(z01 + z00 + z10)
|
||||
area = area + 0.5*dy*dz
|
||||
momy = momy + y*0.5*dy*dz
|
||||
momz = momz + z*0.5*dy*dz
|
||||
elif(len(section[j]) == 1):
|
||||
# y1,z1 -------
|
||||
# |
|
||||
# |
|
||||
# |
|
||||
# y0,z0 -------
|
||||
k = 0
|
||||
y0 = abs(section[j][k].y)
|
||||
z0 = section[j][k].z
|
||||
y1 = abs(section[j+1][k].y)
|
||||
z1 = section[j+1][k].z
|
||||
dy = 0.5 * (y0 + y1)
|
||||
dz = z1 - z0
|
||||
y = 0.25*(y1 + y0)
|
||||
z = 0.25*(z1 + z0)
|
||||
area = area + dy*dz
|
||||
momy = momy + y*dy*dz
|
||||
momz = momz + z*dy*dz
|
||||
# ------------------------------------
|
||||
# StarBoard
|
||||
# ------------------------------------
|
||||
# Get the section
|
||||
section = sections[i]
|
||||
# Get the maximum Z value
|
||||
Z = draft - x*math.tan(angle)
|
||||
# Format section
|
||||
aux = convertSection(section, x, Z)
|
||||
if not aux:
|
||||
areas.append(0.0)
|
||||
My.append(0.0)
|
||||
Mz.append(0.0)
|
||||
continue
|
||||
# Correct by roll angle
|
||||
pts = aux[len(aux)-1]
|
||||
beam = pts[0].y
|
||||
for i in range(1,len(pts)):
|
||||
beam = max(beam, pts[i].y)
|
||||
Z = Z + beam*math.tan(rAngle)
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
areas.append(0.0)
|
||||
My.append(0.0)
|
||||
Mz.append(0.0)
|
||||
continue
|
||||
# Integrate area
|
||||
for j in range(0, len(section)-1):
|
||||
for k in range(0, min(len(section[j])-1, len(section[j+1])-1)):
|
||||
# y11,z11 ------- y01,z01
|
||||
# | |
|
||||
# | |
|
||||
# | |
|
||||
# y10,z10 ------- y00,z00
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = 0.5*((y00 - y10) + (y01 - y11))
|
||||
dz = 0.5*((z01 - z00) + (z11 - z10))
|
||||
y = 0.25*(y00 + y10 + y01 + y11)
|
||||
z = 0.25*(z01 + z00 + z11 + z10)
|
||||
area = area + dy*dz
|
||||
momy = momy - y*dy*dz
|
||||
momz = momz + z*dy*dz
|
||||
if(len(section[j]) < len(section[j+1])):
|
||||
# y01,z01 ------- y11,z11
|
||||
# | __/
|
||||
# | __/
|
||||
# | /
|
||||
# y00,z00
|
||||
k = len(section[j])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y01 - y11
|
||||
dz = z01 - z00
|
||||
y = 0.33*(y00 + y01 + y11)
|
||||
z = 0.33*(z01 + z00 + z11)
|
||||
area = area + 0.5*dy*dz
|
||||
momy = momy - y*0.5*dy*dz
|
||||
momz = momz + z*0.5*dy*dz
|
||||
elif(len(section[j]) > len(section[j+1])):
|
||||
# y01,z01
|
||||
# | \__
|
||||
# | \__
|
||||
# | \
|
||||
# y00,z00 ------- y10,z10
|
||||
k = len(section[j+1])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
dy = y00 - y10
|
||||
dz = z01 - z00
|
||||
y = 0.33*(y00 + y01 + y10)
|
||||
z = 0.33*(z01 + z00 + z10)
|
||||
area = area + 0.5*dy*dz
|
||||
momy = momy - y*0.5*dy*dz
|
||||
momz = momz + z*0.5*dy*dz
|
||||
elif(len(section[j]) == 1):
|
||||
# y1,z1 -------
|
||||
# |
|
||||
# |
|
||||
# |
|
||||
# y0,z0 -------
|
||||
k = 0
|
||||
y0 = abs(section[j][k].y)
|
||||
z0 = section[j][k].z
|
||||
y1 = abs(section[j+1][k].y)
|
||||
z1 = section[j+1][k].z
|
||||
dy = 0.5 * (y0 + y1)
|
||||
dz = z1 - z0
|
||||
y = 0.25*(y1 + y0)
|
||||
z = 0.25*(z1 + z0)
|
||||
area = area + dy*dz
|
||||
momy = momy - y*dy*dz
|
||||
momz = momz + z*dy*dz
|
||||
areas.append(area)
|
||||
My.append(momy)
|
||||
Mz.append(momz)
|
||||
# Add volume & moment if proceed
|
||||
if i > 0:
|
||||
dx = xCoord[i] - xCoord[i-1]
|
||||
x = 0.5*(xCoord[i] + xCoord[i-1])
|
||||
area = 0.5*(areas[i] + areas[i-1])
|
||||
momy = 0.5*(My[i] + My[i-1])
|
||||
momz = 0.5*(Mz[i] + Mz[i-1])
|
||||
vol = vol + area*dx
|
||||
vMy = vMy + momy*dx
|
||||
vMz = vMz + momz*dx
|
||||
# Compute KBT
|
||||
kb[0] = vMy / vol
|
||||
kb[1] = vMz / vol
|
||||
return kb
|
||||
|
||||
def BMT(ship, draft, trim):
|
||||
""" Calculate ship Bouyance center transversal distance.
|
||||
@param ship Selected ship instance
|
||||
@param draft Draft.
|
||||
@param trim Trim in degrees.
|
||||
@return BM Bouyance to metacenter height.
|
||||
"""
|
||||
nRoll = 2
|
||||
maxRoll = 7.0
|
||||
kb0 = KBT(ship,draft,trim)
|
||||
BM = 0.0
|
||||
for i in range(0,nRoll):
|
||||
roll = (maxRoll/nRoll)*(i+1)
|
||||
kb = KBT(ship,draft,trim,roll)
|
||||
# * M
|
||||
# / \
|
||||
# / \ BM ==|> BM = (BB/2) / tan(alpha/2)
|
||||
# / \
|
||||
# *-------*
|
||||
# BB
|
||||
BB = [kb[0] - kb0[0], kb[1] - kb0[1]]
|
||||
BB = math.sqrt(BB[0]*BB[0] + BB[1]*BB[1])
|
||||
BM = BM + 0.5*BB/math.tan(math.radians(0.5*roll)) / nRoll # nRoll is the weight function
|
||||
return BM
|
||||
|
||||
def MainFrameCoeff(ship, draft):
|
||||
""" Calculate main frame coefficient.
|
||||
@param ship Selected ship instance
|
||||
@param draft Draft.
|
||||
@return Main frame coefficient
|
||||
"""
|
||||
sections = Instance.sections(ship)
|
||||
xCoord = ship.xSection[:]
|
||||
cm = 0.0
|
||||
if not sections:
|
||||
return 0.0
|
||||
# Look for nearest to main frame section
|
||||
sectionID = 0
|
||||
X = xCoord[0]
|
||||
for i in range(1, len(sections)):
|
||||
# Get the position of the section
|
||||
x = xCoord[i]
|
||||
if abs(x) < abs(X):
|
||||
sectionID = i
|
||||
X = x
|
||||
# Get the section
|
||||
section = sections[sectionID]
|
||||
if len(section) < 2: # Empty section
|
||||
return 0.0
|
||||
x = X
|
||||
# Get the maximum Z value
|
||||
Z = draft
|
||||
# Format section
|
||||
section = convertSection(section, x, Z)
|
||||
if not section:
|
||||
return 0.0
|
||||
# Integrate area
|
||||
area = 0.0
|
||||
maxY = 0.0
|
||||
for j in range(0, len(section)-1):
|
||||
for k in range(0, min(len(section[j])-1, len(section[j+1])-1)):
|
||||
# y11,z11 ------- y01,z01
|
||||
# | |
|
||||
# | |
|
||||
# | |
|
||||
# y10,z10 ------- y00,z00
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = 0.5*((y00 - y10) + (y01 - y11))
|
||||
dz = 0.5*((z01 - z00) + (z11 - z10))
|
||||
area = area + dy*dz
|
||||
maxY = max([maxY,y00,y10,y01,y11])
|
||||
if(len(section[j]) < len(section[j+1])):
|
||||
# y01,z01 ------- y11,z11
|
||||
# | __/
|
||||
# | __/
|
||||
# | /
|
||||
# y00,z00
|
||||
k = len(section[j])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
y11 = abs(section[j+1][k+1].y)
|
||||
z11 = section[j+1][k+1].z
|
||||
dy = y01 - y11
|
||||
dz = z01 - z00
|
||||
area = area + 0.5*dy*dz
|
||||
maxY = max([maxY,y00,y01,y11])
|
||||
elif(len(section[j]) > len(section[j+1])):
|
||||
# y01,z01
|
||||
# | \__
|
||||
# | \__
|
||||
# | \
|
||||
# y00,z00 ------- y10,z10
|
||||
k = len(section[j+1])-1
|
||||
y00 = abs(section[j][k].y)
|
||||
z00 = section[j][k].z
|
||||
y10 = abs(section[j][k+1].y)
|
||||
z10 = section[j][k+1].z
|
||||
y01 = abs(section[j+1][k].y)
|
||||
z01 = section[j+1][k].z
|
||||
dy = y00 - y10
|
||||
dz = z01 - z00
|
||||
area = area + 0.5*dy*dz
|
||||
maxY = max([maxY,y00,y10,y01])
|
||||
elif(len(section[j]) == 1):
|
||||
# y1,z1 -------
|
||||
# |
|
||||
# |
|
||||
# |
|
||||
# y0,z0 -------
|
||||
k = 0
|
||||
y0 = abs(section[j][k].y)
|
||||
z0 = section[j][k].z
|
||||
y1 = abs(section[j+1][k].y)
|
||||
z1 = section[j+1][k].z
|
||||
dy = 0.5 * (y0 + y1)
|
||||
dz = z1 - z0
|
||||
area = area + dy*dz
|
||||
maxY = max([maxY,y0,y1])
|
||||
if maxY*draft > 0.0:
|
||||
cm = area / (maxY*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, draft, trim):
|
||||
""" Use all hydrostatics tools to define a hydrostatics
|
||||
point.
|
||||
@param ship Selected ship instance
|
||||
@param draft Draft.
|
||||
@param trim Trim in degrees.
|
||||
"""
|
||||
# Hydrostatics computation
|
||||
areasData = Displacement(ship,draft,trim)
|
||||
wettedArea = WettedArea(ship,draft,trim)
|
||||
moment = Moment(ship,draft,trim,areasData[1],areasData[2])
|
||||
farea = FloatingArea(ship,draft,trim)
|
||||
kb = KBT(ship,draft,trim)
|
||||
bm = BMT(ship,draft,trim)
|
||||
cm = MainFrameCoeff(ship,draft)
|
||||
# Store final data
|
||||
self.draft = draft
|
||||
self.trim = trim
|
||||
self.disp = areasData[1]
|
||||
self.xcb = areasData[2]
|
||||
self.wet = wettedArea
|
||||
self.farea = farea[0]
|
||||
self.mom = moment
|
||||
self.KBt = kb[1]
|
||||
self.BMt = bm
|
||||
self.Cb = areasData[3]
|
||||
self.Cf = farea[1]
|
||||
self.Cm = cm
|
||||
|
|
|
@ -21,3 +21,17 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
# FreeCAD modules
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
# Qt libraries
|
||||
from PyQt4 import QtGui,QtCore
|
||||
|
||||
# Main object
|
||||
import TaskPanel
|
||||
|
||||
def load():
|
||||
""" Loads the tool """
|
||||
TaskPanel.createTask()
|
||||
|
|
104
src/Mod/Ship/shipLoadExample/TaskPanel.py
Normal file
104
src/Mod/Ship/shipLoadExample/TaskPanel.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2011, 2012 *
|
||||
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
# FreeCAD modules
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
# Qt library
|
||||
from PyQt4 import QtGui,QtCore
|
||||
# Module
|
||||
from shipUtils import Paths, Translator
|
||||
from surfUtils import Geometry
|
||||
|
||||
class TaskPanel:
|
||||
def __init__(self):
|
||||
self.ui = Paths.modulePath() + "/shipLoadExample/TaskPanel.ui"
|
||||
|
||||
def accept(self):
|
||||
path = Paths.modulePath() + "/Examples/"
|
||||
if(self.form.ship.currentIndex() == 0): # s60 from Iowa University
|
||||
App.open(path + "s60.fcstd")
|
||||
elif(self.form.ship.currentIndex() == 1): # Barehull 5415
|
||||
App.open(path + "barehull5415.fcstd")
|
||||
elif(self.form.ship.currentIndex() == 2): # s60 (Katamaran)
|
||||
App.open(path + "s60_katamaran.fcstd")
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
return True
|
||||
|
||||
def clicked(self, index):
|
||||
pass
|
||||
|
||||
def open(self):
|
||||
pass
|
||||
|
||||
def needsFullSpace(self):
|
||||
return True
|
||||
|
||||
def isAllowedAlterSelection(self):
|
||||
return False
|
||||
|
||||
def isAllowedAlterView(self):
|
||||
return True
|
||||
|
||||
def isAllowedAlterDocument(self):
|
||||
return False
|
||||
|
||||
def helpRequested(self):
|
||||
pass
|
||||
|
||||
def setupUi(self):
|
||||
mw = self.getMainWindow()
|
||||
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
||||
form.ship = form.findChild(QtGui.QComboBox, "Ship")
|
||||
form.mainLogo = form.findChild(QtGui.QLabel, "MainLogo")
|
||||
iconPath = Paths.iconsPath() + "/Ico.xpm"
|
||||
form.mainLogo.setPixmap(QtGui.QPixmap(iconPath))
|
||||
self.form = form
|
||||
self.retranslateUi()
|
||||
|
||||
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 retranslateUi(self):
|
||||
""" Set user interface locale strings.
|
||||
"""
|
||||
self.form.setWindowTitle(Translator.translate("Load example ship"))
|
||||
self.form.findChild(QtGui.QGroupBox, "ShipSelectionBox").setTitle(Translator.translate("Select ship example geometry"))
|
||||
|
||||
def createTask():
|
||||
panel = TaskPanel()
|
||||
Gui.Control.showDialog(panel)
|
||||
if panel.setupUi():
|
||||
Gui.Control.closeDialog(panel)
|
||||
return None
|
||||
return panel
|
120
src/Mod/Ship/shipLoadExample/TaskPanel.ui
Normal file
120
src/Mod/Ship/shipLoadExample/TaskPanel.ui
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TaskPanel</class>
|
||||
<widget class="QWidget" name="TaskPanel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>260</width>
|
||||
<height>397</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Load example ship</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="MainLogo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>128</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>../Icons/Ico.xpm</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignHCenter|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ShipSelectionBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>240</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Select ship example geometry</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>241</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QComboBox" name="Ship">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Serie 60 from Iowa University</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Barehull 5145</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Serie 60 (Katamaran)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
36
src/Mod/Ship/shipLoadExample/__init__.py
Normal file
36
src/Mod/Ship/shipLoadExample/__init__.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2011, 2012 *
|
||||
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
# FreeCAD modules
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
# Qt libraries
|
||||
from PyQt4 import QtGui,QtCore
|
||||
|
||||
# Main object
|
||||
import TaskPanel
|
||||
|
||||
def load():
|
||||
""" Loads the tool """
|
||||
TaskPanel.createTask()
|
|
@ -57,7 +57,8 @@ def Plot(scale, sections, shape):
|
|||
border = border.oldFuse(edges[i]) # Only group objects, don't try to build more complex entities
|
||||
border = border.oldFuse(edges[i].mirror(Vector(0.0, 0.0, 0.0),Vector(0.0, 1.0, 0.0)))
|
||||
# Fuse sections & borders
|
||||
obj = sections.oldFuse(border)
|
||||
# obj = sections.oldFuse(border)
|
||||
obj = border.oldFuse(sections)
|
||||
# Send to 3D view
|
||||
Part.show(obj)
|
||||
objs = FreeCAD.ActiveDocument.Objects
|
||||
|
|
|
@ -118,7 +118,7 @@ class TaskPanel:
|
|||
# Get selected objects
|
||||
selObjs = Geometry.getSelectedObjs()
|
||||
if not selObjs:
|
||||
msg = Translator.translate("Ship instance must be selected (any object selected)\n")
|
||||
msg = Translator.translate("Ship instance must be selected (no object selected)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
for i in range(0,len(selObjs)):
|
||||
|
@ -138,7 +138,7 @@ class TaskPanel:
|
|||
self.ship = obj
|
||||
# Test if any valid ship was selected
|
||||
if not self.ship:
|
||||
msg = Translator.translate("Ship instance must be selected (any valid ship found at selected objects)\n")
|
||||
msg = Translator.translate("Ship instance must be selected (no valid ship found at selected objects)\n")
|
||||
App.Console.PrintError(msg)
|
||||
return True
|
||||
# Load sections (if exist)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
def isAprox(a,b,tol=0.0001):
|
||||
def isAprox(a,b,tol=0.000001):
|
||||
"""returns if a value is into (b-tol,b+tol)
|
||||
@param a Value to compare.
|
||||
@param b Center of valid interval
|
||||
|
@ -32,7 +32,7 @@ def isAprox(a,b,tol=0.0001):
|
|||
return True
|
||||
return False
|
||||
|
||||
def isSamePoint(a,b,tol=0.0001):
|
||||
def isSamePoint(a,b,tol=0.000001):
|
||||
"""returns if two points are the same with a provided tolerance
|
||||
@param a Point to compare.
|
||||
@param b Reference point.
|
||||
|
|
Loading…
Reference in New Issue
Block a user