diff --git a/src/Mod/Ship/Ship.py b/src/Mod/Ship/Ship.py
index e0a3aefe5..bea30aae5 100644
--- a/src/Mod/Ship/Ship.py
+++ b/src/Mod/Ship/Ship.py
@@ -30,4 +30,5 @@ __doc__="The Ships module provide a set of tools to make some specific Naval" \
" Architecture computations"
from shipCreateShip.Tools import createShip
-from shipHydrostatics.Tools import areas
\ No newline at end of file
+from shipHydrostatics.Tools import areas, displacement, wettedArea, moment,
+ floatingArea, BMT, mainFrameCoeff
\ No newline at end of file
diff --git a/src/Mod/Ship/shipAreasCurve/PlotAux.py b/src/Mod/Ship/shipAreasCurve/PlotAux.py
index e83578e28..35c5df9a6 100644
--- a/src/Mod/Ship/shipAreasCurve/PlotAux.py
+++ b/src/Mod/Ship/shipAreasCurve/PlotAux.py
@@ -89,9 +89,9 @@ class Plot(object):
addInfo = ("$XCB = {0} \\; \\mathrm{{m}}$\n"
"$Area_{{max}} = {1} \\; \\mathrm{{m}}^2$\n"
"$\\bigtriangleup = {2} \\; \\mathrm{{tons}}$".format(
- xcb,
+ xcb.getValueAs("m").Value,
maxArea,
- disp))
+ disp.getValueAs("kg").Value / 1000.0))
ax.text(0.0,
0.01 * maxArea,
addInfo,
diff --git a/src/Mod/Ship/shipAreasCurve/Preview.py b/src/Mod/Ship/shipAreasCurve/Preview.py
index 5a13718b8..44fbd6cf9 100644
--- a/src/Mod/Ship/shipAreasCurve/Preview.py
+++ b/src/Mod/Ship/shipAreasCurve/Preview.py
@@ -55,11 +55,11 @@ class Preview(object):
point = Base.Vector(x, y, 0.0)
plane = Part.makePlane(L, B, point, Base.Vector(0, 0, 1))
plane.rotate(Base.Vector(0, 0, 0), Base.Vector(0, 1, 0), trim)
- plane.translate(Base.Vector(0, 0, draft * Units.Metre.Value))
+ plane.translate(Base.Vector(0, 0, draft))
Part.show(plane)
objs = FreeCAD.ActiveDocument.Objects
self.obj = objs[len(objs) - 1]
- self.obj.Label = 'FreeSurface'
+ self.obj.Label = 'FreeSurfaceHelper'
guiObj = FreeCADGui.ActiveDocument.getObject(self.obj.Name)
guiObj.ShapeColor = (0.4, 0.8, 0.85)
guiObj.Transparency = 50
@@ -69,4 +69,4 @@ class Preview(object):
if not self.obj:
return
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
- self.obj = None
+ self.obj = None
\ No newline at end of file
diff --git a/src/Mod/Ship/shipAreasCurve/TaskPanel.py b/src/Mod/Ship/shipAreasCurve/TaskPanel.py
index d3308ae7a..c32db44f5 100644
--- a/src/Mod/Ship/shipAreasCurve/TaskPanel.py
+++ b/src/Mod/Ship/shipAreasCurve/TaskPanel.py
@@ -55,12 +55,11 @@ class TaskPanel:
trim = Units.parseQuantity(Locale.fromString(form.trim.text()))
num = form.num.value()
- data = Hydrostatics.displacement(self.ship,
- draft.getValueAs("m").Value,
- 0.0,
- trim.getValueAs("deg").Value)
- disp = data[0]
- xcb = data[1].x
+ disp, B, _ = Hydrostatics.displacement(self.ship,
+ draft,
+ Units.parseQuantity("0 deg"),
+ trim)
+ xcb = Units.Quantity(B.x, Units.Length)
data = Hydrostatics.areas(self.ship,
num,
draft=draft,
@@ -245,24 +244,11 @@ class TaskPanel:
None,
QtGui.QApplication.UnicodeUTF8))
- def clampLength(self, widget, val_min, val_max, val):
- if val >= val_min and val <= val_max:
+ def clampValue(self, widget, val_min, val_max, val):
+ if val_min <= val <= val_max:
return val
- input_format = USys.getLengthFormat()
val = min(val_max, max(val_min, val))
- qty = Units.Quantity('{} m'.format(val))
- widget.setText(Locale.toString(input_format.format(
- qty.getValueAs(USys.getLengthUnits()).Value)))
- return val
-
- def clampAngle(self, widget, val_min, val_max, val):
- if val >= val_min and val <= val_max:
- return val
- input_format = USys.getAngleFormat()
- val = min(val_max, max(val_min, val))
- qty = Units.Quantity('{} deg'.format(val))
- widget.setText(Locale.toString(input_format.format(
- qty.getValueAs(USys.getLengthUnits()).Value)))
+ widget.setText(val.UserString)
return val
def onData(self, value):
@@ -279,32 +265,24 @@ class TaskPanel:
# Get the values (or fix them in bad setting case)
try:
- draft = Units.Quantity(Locale.fromString(
- form.draft.text())).getValueAs('m').Value
+ draft = Units.parseQuantity(Locale.fromString(form.draft.text()))
except:
- draft = self.ship.Draft.getValueAs(USys.getLengthUnits()).Value
- input_format = USys.getLengthFormat()
- qty = Units.Quantity('{} m'.format(draft))
- widget.setText(Locale.toString(input_format.format(
- qty.getValueAs(USys.getLengthUnits()).Value)))
+ draft = self.ship.Draft
+ form.draft.setText(draft.UserString)
try:
- trim = Units.Quantity(Locale.fromString(
- form.trim.text())).getValueAs('deg').Value
+ trim = Units.parseQuantity(Locale.fromString(form.trim.text()))
except:
- trim = 0.0
- input_format = USys.getAngleFormat()
- qty = Units.Quantity('{} deg'.format(trim))
- widget.setText(Locale.toString(input_format.format(
- qty.getValueAs(USys.getLengthUnits()).Value)))
+ trim = Units.parseQuantity("0 deg")
+ form.trim.setText(trim.UserString)
bbox = self.ship.Shape.BoundBox
- draft_min = bbox.ZMin / Units.Metre.Value
- draft_max = bbox.ZMax / Units.Metre.Value
- draft = self.clampLength(form.draft, draft_min, draft_max, draft)
+ draft_min = Units.Quantity(bbox.ZMin, Units.Length)
+ draft_max = Units.Quantity(bbox.ZMax, Units.Length)
+ draft = self.clampValue(form.draft, draft_min, draft_max, draft)
- trim_min = -180.0
- trim_max = 180.0
- trim = self.clampAngle(form.trim, trim_min, trim_max, trim)
+ trim_min = Units.parseQuantity("-180 deg")
+ trim_max = Units.parseQuantity("180 deg")
+ trim = self.clampValue(form.trim, trim_min, trim_max, trim)
self.onUpdate()
self.preview.update(draft, trim, self.ship)
@@ -319,40 +297,39 @@ class TaskPanel:
form.trim = self.widget(QtGui.QLineEdit, "Trim")
form.output = self.widget(QtGui.QTextEdit, "OutputData")
- draft = Units.Quantity(Locale.fromString(
- form.draft.text())).getValueAs('m').Value
- trim = Units.Quantity(Locale.fromString(
- form.trim.text())).getValueAs('deg').Value
+ draft = Units.parseQuantity(Locale.fromString(form.draft.text()))
+ trim = Units.parseQuantity(Locale.fromString(form.trim.text()))
# Calculate the drafts at each perpendicular
- angle = math.radians(trim)
+ angle = trim.getValueAs("rad").Value
L = self.ship.Length.getValueAs('m').Value
B = self.ship.Breadth.getValueAs('m').Value
- draftAP = draft + 0.5 * L * math.tan(angle)
+ draftAP = draft + 0.5 * self.ship.Length * math.tan(angle)
if draftAP < 0.0:
draftAP = 0.0
- draftFP = draft - 0.5 * L * math.tan(angle)
+ draftFP = draft - 0.5 * self.ship.Length * math.tan(angle)
if draftFP < 0.0:
draftFP = 0.0
# Calculate the involved hydrostatics
- data = Hydrostatics.displacement(self.ship,
- draft,
- 0.0,
- trim)
+ disp, B, _ = Hydrostatics.displacement(self.ship,
+ draft,
+ Units.parseQuantity("0 deg"),
+ trim)
+ xcb = Units.Quantity(B.x, Units.Length)
# Setup the html string
- string = 'L = {0} [m]
'.format(L)
- string = string + 'B = {0} [m]
'.format(B)
- string = string + 'T = {0} [m]
'.format(draft)
- string = string + 'Trim = {0} [degrees]
'.format(trim)
- string = string + 'TAP = {0} [m]
'.format(draftAP)
- string = string + 'TFP = {0} [m]
'.format(draftFP)
+ string = u'L = {0}
'.format(self.ship.Length.UserString)
+ string += u'B = {0}
'.format(self.ship.Breadth.UserString)
+ string += u'T = {0}
'.format(draft.UserString)
+ string += u'Trim = {0}
'.format(trim.UserString)
+ string += u'TAP = {0}
'.format(draftAP.UserString)
+ string += u'TFP = {0}
'.format(draftFP.UserString)
dispText = QtGui.QApplication.translate(
"ship_areas",
'Displacement',
None,
QtGui.QApplication.UnicodeUTF8)
- string = string + dispText + ' = {0} [ton]
'.format(data[0])
- string = string + 'XCB = {0} [m]'.format(data[1].x)
+ string += dispText + u' = {0}
'.format(disp.UserString)
+ string += u'XCB = {0}'.format(xcb.UserString)
form.output.setHtml(string)
def save(self):
@@ -363,10 +340,8 @@ class TaskPanel:
form.trim = self.widget(QtGui.QLineEdit, "Trim")
form.num = self.widget(QtGui.QSpinBox, "Num")
- draft = Units.Quantity(Locale.fromString(
- form.draft.text())).getValueAs('m').Value
- trim = Units.Quantity(Locale.fromString(
- form.trim.text())).getValueAs('deg').Value
+ draft = Units.parseQuantity(Locale.fromString(form.draft.text()))
+ trim = Units.parseQuantity(Locale.fromString(form.trim.text()))
num = form.num.value()
props = self.ship.PropertiesList
@@ -385,7 +360,7 @@ class TaskPanel:
"AreaCurveDraft",
"Ship",
tooltip)
- self.ship.AreaCurveDraft = '{} m'.format(draft)
+ self.ship.AreaCurveDraft = draft
try:
props.index("AreaCurveTrim")
except ValueError:
@@ -401,7 +376,7 @@ class TaskPanel:
"AreaCurveTrim",
"Ship",
tooltip)
- self.ship.AreaCurveTrim = '{} deg'.format(trim)
+ self.ship.AreaCurveTrim = trim
try:
props.index("AreaCurveNum")
except ValueError:
@@ -426,4 +401,4 @@ def createTask():
if panel.setupUi():
Gui.Control.closeDialog(panel)
return None
- return panel
+ return panel
\ No newline at end of file
diff --git a/src/Mod/Ship/shipGZ/Tools.py b/src/Mod/Ship/shipGZ/Tools.py
index 6c91a54d4..a090b1ed3 100644
--- a/src/Mod/Ship/shipGZ/Tools.py
+++ b/src/Mod/Ship/shipGZ/Tools.py
@@ -121,8 +121,12 @@ def solve_point(W, COG, TW, VOLS, ship, tanks, roll, var_trim=True):
for i in range(MAX_EQUILIBRIUM_ITERS):
# Get the displacement, and the bouyance application point
- disp, B, Cb = Hydrostatics.displacement(ship, draft, roll, trim)
- disp *= 1000.0 * G
+ disp, B, _ = Hydrostatics.displacement(ship,
+ draft * Units.Metre,
+ roll * Units.Degree,
+ trim * Units.Degree)
+ disp = disp.getValueAs("kg").Value * G
+ B.multiply(1.0 / Units.Metre.Value)
# Add the tanks effect on the center of gravity
cog = Vector(COG.x * W, COG.y * W, COG.z * W)
for i,t in enumerate(tanks):
diff --git a/src/Mod/Ship/shipHydrostatics/PlotAux.py b/src/Mod/Ship/shipHydrostatics/PlotAux.py
index 532d16f30..51702c1c4 100644
--- a/src/Mod/Ship/shipHydrostatics/PlotAux.py
+++ b/src/Mod/Ship/shipHydrostatics/PlotAux.py
@@ -84,11 +84,11 @@ class Plot(object):
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)
+ disp.append(self.points[i].disp.getValueAs("kg").Value / 1000.0)
+ draft.append(self.points[i].draft.getValueAs("m").Value)
+ warea.append(self.points[i].wet.getValueAs("m^2").Value)
+ t1cm.append(self.points[i].mom.getValueAs("kg*m").Value / 1000.0)
+ xcb.append(self.points[i].xcb.getValueAs("m").Value)
axes = Plot.axesList()
for ax in axes:
@@ -166,11 +166,11 @@ class Plot(object):
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)
+ disp.append(self.points[i].disp.getValueAs("kg").Value / 1000.0)
+ draft.append(self.points[i].draft.getValueAs("m").Value)
+ farea.append(self.points[i].farea.getValueAs("m^2").Value)
+ kbt.append(self.points[i].KBt.getValueAs("m").Value)
+ bmt.append(self.points[i].BMt.getValueAs("m").Value)
axes = Plot.axesList()
for ax in axes:
@@ -248,8 +248,8 @@ class Plot(object):
cf = []
cm = []
for i in range(len(self.points)):
- disp.append(self.points[i].disp)
- draft.append(self.points[i].draft)
+ disp.append(self.points[i].disp.getValueAs("kg").Value / 1000.0)
+ draft.append(self.points[i].draft.getValueAs("m").Value)
cb.append(self.points[i].Cb)
cf.append(self.points[i].Cf)
cm.append(self.points[i].Cm)
@@ -322,17 +322,28 @@ class Plot(object):
# Print the data
for i in range(len(self.points)):
point = self.points[i]
- s.set("A{}".format(i + 2), str(point.disp))
- s.set("B{}".format(i + 2), str(point.draft))
- s.set("C{}".format(i + 2), str(point.wet))
- s.set("D{}".format(i + 2), str(point.mom))
- s.set("E{}".format(i + 2), str(point.farea))
- s.set("F{}".format(i + 2), str(point.xcb))
- s.set("G{}".format(i + 2), str(point.KBt))
- s.set("H{}".format(i + 2), str(point.BMt))
- s.set("I{}".format(i + 2), str(point.Cb))
- s.set("J{}".format(i + 2), str(point.Cf))
- s.set("K{}".format(i + 2), str(point.Cm))
+ s.set("A{}".format(i + 2),
+ str(point.disp.getValueAs("kg").Value / 1000.0))
+ s.set("B{}".format(i + 2),
+ str(point.draft.getValueAs("m").Value))
+ s.set("C{}".format(i + 2),
+ str(point.wet.getValueAs("m^2").Value))
+ s.set("D{}".format(i + 2),
+ str(point.mom.getValueAs("kg*m").Value / 1000.0))
+ s.set("E{}".format(i + 2),
+ str(point.farea.getValueAs("m^2").Value))
+ s.set("F{}".format(i + 2),
+ str(point.xcb.getValueAs("m").Value))
+ s.set("G{}".format(i + 2),
+ str(point.KBt.getValueAs("m").Value))
+ s.set("H{}".format(i + 2),
+ str(point.BMt.getValueAs("m").Value))
+ s.set("I{}".format(i + 2),
+ str(point.Cb))
+ s.set("J{}".format(i + 2),
+ str(point.Cf))
+ s.set("K{}".format(i + 2),
+ str(point.Cm))
# Recompute
FreeCAD.activeDocument().recompute()
\ No newline at end of file
diff --git a/src/Mod/Ship/shipHydrostatics/TaskPanel.py b/src/Mod/Ship/shipHydrostatics/TaskPanel.py
index f5c9d8107..c91f40912 100644
--- a/src/Mod/Ship/shipHydrostatics/TaskPanel.py
+++ b/src/Mod/Ship/shipHydrostatics/TaskPanel.py
@@ -57,12 +57,9 @@ class TaskPanel:
form.maxDraft = self.widget(QtGui.QLineEdit, "MaxDraft")
form.nDraft = self.widget(QtGui.QSpinBox, "NDraft")
- trim = Units.Quantity(Locale.fromString(
- form.trim.text())).getValueAs('deg').Value
- min_draft = Units.Quantity(Locale.fromString(
- form.minDraft.text())).getValueAs('m').Value
- max_draft = Units.Quantity(Locale.fromString(
- form.maxDraft.text())).getValueAs('m').Value
+ trim = Units.parseQuantity(Locale.fromString(form.trim.text()))
+ min_draft = Units.parseQuantity(Locale.fromString(form.minDraft.text()))
+ max_draft = Units.parseQuantity(Locale.fromString(form.maxDraft.text()))
n_draft = form.nDraft.value()
draft = min_draft
@@ -72,7 +69,6 @@ class TaskPanel:
draft = draft + dDraft
drafts.append(draft)
- # Compute data
# Get external faces
self.loop = QtCore.QEventLoop()
self.timer = QtCore.QTimer()
@@ -94,6 +90,7 @@ class TaskPanel:
App.Console.PrintError(msg + '\n')
return False
faces = Part.makeShell(faces)
+
# Get the hydrostatics
msg = QtGui.QApplication.translate(
"ship_console",
diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py
index 6d729e50f..7c06842d0 100644
--- a/src/Mod/Ship/shipHydrostatics/Tools.py
+++ b/src/Mod/Ship/shipHydrostatics/Tools.py
@@ -34,7 +34,7 @@ from shipUtils import Math
import shipUtils.Units as USys
-DENS = 1.025 # [tons/m3], salt water
+DENS = Units.parseQuantity("1025 kg/m^3") # Salt water
COMMON_BOOLEAN_ITERATIONS = 10
@@ -49,9 +49,11 @@ def placeShipShape(shape, draft, roll, trim):
roll -- Roll angle
trim -- Trim angle
- Returned value:
- The same transformed input shape. Just for debugging purposes, you can
- discard it.
+ Returned values:
+ shape -- The same transformed input shape. Just for debugging purposes, you
+ can discard it.
+ base_z -- The new base z coordinate (after applying the roll angle). Useful
+ if you want to revert back the transformation
"""
# Roll the ship. In order to can deal with large roll angles, we are
# proceeding as follows:
@@ -66,16 +68,21 @@ def placeShipShape(shape, draft, roll, trim):
shape.translate(Vector(draft * math.sin(math.radians(trim)), 0.0, 0.0))
shape.translate(Vector(0.0, 0.0, -draft))
- return shape
+ return shape, base_z
-def getUnderwaterSide(shape):
+def getUnderwaterSide(shape, force=True):
"""Get the underwater shape, simply cropping the provided shape by the z=0
free surface plane.
Position arguments:
shape -- Solid shape to be cropped
+ Keyword arguments:
+ force -- True if in case the common boolean operation fails, i.e. returns
+ no solids, the tool should retry it slightly moving the free surface. False
+ otherwise. (True by default)
+
Returned value:
Cropped shape. It is not modifying the input shape
"""
@@ -109,7 +116,7 @@ def getUnderwaterSide(shape):
"UnderwaterSideHelper")
common.Shapes = [orig, box]
App.ActiveDocument.recompute()
- if len(common.Shape.Solids) == 0:
+ if force and len(common.Shape.Solids) == 0:
# The common operation is failing, let's try moving a bit the free
# surface
msg = QtGui.QApplication.translate(
@@ -139,7 +146,7 @@ def getUnderwaterSide(shape):
def areas(ship, n, draft=None,
roll=Units.parseQuantity("0 deg"),
trim=Units.parseQuantity("0 deg")):
- """ Compute the ship transversal areas
+ """Compute the ship transversal areas
Position arguments:
ship -- Ship object (see createShip)
@@ -161,11 +168,8 @@ def areas(ship, n, draft=None,
if draft is None:
draft = ship.Draft
- # Manipulate a copy of the ship shape
- shape = getUnderwaterSide(placeShipShape(ship.Shape.copy(),
- draft,
- roll,
- trim))
+ shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim)
+ shape = getUnderwaterSide(shape)
# Sections distance computation
bbox = shape.BoundBox
@@ -191,6 +195,12 @@ def areas(ship, n, draft=None,
try:
f = Part.Face(shape.slice(Vector(1,0,0), x))
except Part.OCCError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "Part.OCCError: Transversal area computation failed",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
areas.append((Units.Quantity(x, Units.Length),
Units.Quantity(0.0, Units.Area)))
continue
@@ -204,397 +214,328 @@ def areas(ship, n, draft=None,
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
+def displacement(ship, draft=None,
+ roll=Units.parseQuantity("0 deg"),
+ trim=Units.parseQuantity("0 deg")):
+ """Compute the ship displacement
+
+ Position arguments:
+ ship -- Ship object (see createShip)
+
+ Keyword arguments:
+ draft -- Ship draft (Design ship draft by default)
+ roll -- Roll angle (0 degrees by default)
+ trim -- Trim angle (0 degrees by default)
+
+ Returned values:
+ disp -- The ship displacement (a density of the water of 1025 kg/m^3 is
+ assumed)
+ B -- Bouyance application point, i.e. Center of mass of the underwater side
+ Cb -- Block coefficient
+
+ The Bouyance center is refered to the original ship position.
"""
- # We will take a duplicate of ship shape in order to conviniently
- # manipulate it
- shape = ship.Shape.copy()
- _draft = draft * Units.Metre.Value
- # Roll the ship. In order to can deal with large roll angles, we are
- # proceeding as follows:
- # 1.- Applying the roll with respect the base line
- # 2.- Recentering the ship in the y direction
- # 3.- Readjusting the base line
- shape.rotate(Vector(0.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0), roll)
- base_z = shape.BoundBox.ZMin
- shape.translate(Vector(0.0,
- _draft * math.sin(math.radians(roll)),
- -base_z))
- # Trim and yaw the ship. In this case we only need to correct the x
- # direction
- shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
- shape.translate(Vector(_draft * math.sin(math.radians(trim)), 0.0, 0.0))
- shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, 0.0, 1.0), yaw)
- shape.translate(Vector(0.0, 0.0, -_draft))
- Part.show(shape)
- ship_shape = App.ActiveDocument.Objects[-1]
+ if draft is None:
+ draft = ship.Draft
- bbox = shape.BoundBox
- xmin = bbox.XMin
- xmax = bbox.XMax
- ymin = bbox.YMin
- ymax = bbox.YMax
- zmin = bbox.ZMin
- zmax = bbox.ZMax
- # Create the "sea" box to intersect the ship
- L = xmax - xmin
- B = ymax - ymin
- H = zmax - zmin
-
- box = App.ActiveDocument.addObject("Part::Box","Box")
- length_format = USys.getLengthFormat()
- box.Placement = Placement(Vector(xmin - L, ymin - B, zmin - H),
- Rotation(App.Vector(0,0,1),0))
- box.Length = length_format.format(3.0 * L)
- box.Width = length_format.format(3.0 * B)
- box.Height = length_format.format(- zmin + H)
-
- App.ActiveDocument.recompute()
- common = App.activeDocument().addObject("Part::MultiCommon",
- "DisplacementHelper")
- common.Shapes = [ship_shape, box]
- App.ActiveDocument.recompute()
- if len(common.Shape.Solids) == 0:
- # The common operation is failing, let's try moving a bit the free
- # surface
- msg = QtGui.QApplication.translate(
- "ship_console",
- "Boolean operation failed. The tool is retrying that slightly"
- " moving the free surface position",
- None,
- QtGui.QApplication.UnicodeUTF8)
- App.Console.PrintWarning(msg + '\n')
- random_bounds = 0.01 * H
- i = 0
- while len(common.Shape.Solids) == 0 and i < COMMON_BOOLEAN_ITERATIONS:
- i += 1
- box.Height = length_format.format(
- - zmin + H + random.uniform(-random_bounds, random_bounds))
- App.ActiveDocument.recompute()
+ shape, base_z = placeShipShape(ship.Shape.copy(), draft, roll, trim)
+ shape = getUnderwaterSide(shape)
vol = 0.0
cog = Vector()
- if len(common.Shape.Solids) > 0:
- for solid in common.Shape.Solids:
- vol += solid.Volume / Units.Metre.Value**3
+ if len(shape.Solids) > 0:
+ for solid in shape.Solids:
+ vol += solid.Volume
sCoG = solid.CenterOfMass
- cog.x = cog.x + sCoG.x * solid.Volume / Units.Metre.Value**4
- cog.y = cog.y + sCoG.y * solid.Volume / Units.Metre.Value**4
- cog.z = cog.z + sCoG.z * solid.Volume / Units.Metre.Value**4
+ cog.x = cog.x + sCoG.x * solid.Volume
+ cog.y = cog.y + sCoG.y * solid.Volume
+ cog.z = cog.z + sCoG.z * solid.Volume
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
- App.ActiveDocument.removeObject(common.Name)
- App.ActiveDocument.removeObject(ship_shape.Name)
- App.ActiveDocument.removeObject(box.Name)
- App.ActiveDocument.recompute()
+ bbox = shape.BoundBox
+ Vol = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin) * abs(bbox.ZMin)
- # Undo the transformations
+ # Undo the transformations on the bouyance point
B = Part.Point(Vector(cog.x, cog.y, cog.z))
m = Matrix()
m.move(Vector(0.0, 0.0, draft))
- m.rotateZ(-math.radians(yaw))
m.move(Vector(-draft * math.sin(math.radians(trim)), 0.0, 0.0))
m.rotateY(math.radians(trim))
m.move(Vector(0.0,
-draft * math.sin(math.radians(roll)),
- base_z / Units.Metre.Value))
+ base_z))
m.rotateX(-math.radians(roll))
B.transform(m)
- # Return the computed data
- return [DENS*vol, Vector(B.X, B.Y, B.Z), 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
-
- shape = shape.copy()
- _draft = draft * Units.Metre.Value
- shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
- shape.translate(Vector(0.0, 0.0, -_draft))
-
- bbox = shape.BoundBox
- xmin = bbox.XMin
- xmax = bbox.XMax
- ymin = bbox.YMin
- ymax = bbox.YMax
-
- # Create the "sea" box
- L = xmax - xmin
- B = ymax - ymin
- p = Vector(xmin - L, ymin - B, bbox.ZMin - 1.0)
try:
- box = Part.makeBox(3.0 * L, 3.0 * B, - bbox.ZMin + 1.0, p)
- except Part.OCCError:
- return 0.0
-
- for f in shape.Faces:
- try:
- common = box.common(f)
- except Part.OCCError:
- continue
- area = area + common.Area
- return area / Units.Metre.Value**2
+ cb = vol / Vol
+ except ZeroDivisionError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "ZeroDivisionError: Null volume found during the displacement"
+ " computation!",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
+ cb = 0.0
-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.
+ # Return the computed data
+ return (DENS * Units.Quantity(vol, Units.Volume),
+ Vector(B.X, B.Y, B.Z),
+ cb)
+
+
+def wettedArea(shape, draft, roll=Units.parseQuantity("0 deg"),
+ trim=Units.parseQuantity("0 deg")):
+ """Compute the ship wetted area
+
+ Position arguments:
+ shape -- External faces of the ship hull
+ draft -- Ship draft
+
+ Keyword arguments:
+ roll -- Roll angle (0 degrees by default)
+ trim -- Trim angle (0 degrees by default)
+
+ Returned value:
+ The wetted area, i.e. The underwater side area
"""
+ shape, _ = placeShipShape(shape.copy(), draft, roll, trim)
+ shape = getUnderwaterSide(shape, force=False)
+
+ area = 0.0
+ for f in shape.Faces:
+ area = area + f.Area
+ return Units.Quantity(area, Units.Area)
+
+
+def moment(ship, draft=None,
+ roll=Units.parseQuantity("0 deg"),
+ trim=Units.parseQuantity("0 deg")):
+ """Compute the moment required to trim the ship 1cm
+
+ Position arguments:
+ ship -- Ship object (see createShip)
+
+ Keyword arguments:
+ draft -- Ship draft (Design ship draft by default)
+ roll -- Roll angle (0 degrees by default)
+ trim -- Trim angle (0 degrees by default)
+
+ Returned value:
+ Moment required to trim the ship 1cm. Such moment is positive if it cause a
+ positive trim angle. The moment is expressed as a mass by a distance, not as
+ a force by a distance
+ """
+ disp_orig, B_orig, _ = displacement(ship, draft, roll, trim)
+ xcb_orig = Units.Quantity(B_orig.x, Units.Length)
+
factor = 10.0
- angle = factor * math.degrees(math.atan2(
- 0.01,
- 0.5 * ship.Length.getValueAs('m').Value))
- newTrim = trim + angle
- data = displacement(ship, draft, 0.0, newTrim, 0.0)
- mom0 = -disp * xcb
- mom1 = -data[0] * data[1].x
+ x = 0.5 * ship.Length.getValueAs('cm').Value
+ y = 1.0
+ angle = math.atan2(y, x) * Units.Radian
+ trim_new = trim + factor * angle
+ disp_new, B_new, _ = displacement(ship, draft, roll, trim_new)
+ xcb_new = Units.Quantity(B_new.x, Units.Length)
+
+ mom0 = -disp_orig * xcb_orig
+ mom1 = -disp_new * xcb_new
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
+def floatingArea(ship, draft=None,
+ roll=Units.parseQuantity("0 deg"),
+ trim=Units.parseQuantity("0 deg")):
+ """Compute the ship floating area
- shape = ship.Shape.copy()
- _draft = draft * Units.Metre.Value
- shape.rotate(Vector(0.0, 0.0, 0.0), Vector(0.0, -1.0, 0.0), trim)
- shape.translate(Vector(0.0, 0.0, -_draft))
+ Position arguments:
+ ship -- Ship object (see createShip)
+
+ Keyword arguments:
+ draft -- Ship draft (Design ship draft by default)
+ roll -- Roll angle (0 degrees by default)
+ trim -- Trim angle (0 degrees by default)
+
+ Returned values:
+ area -- Ship floating area
+ cf -- Floating area coefficient
+ """
+ if draft is None:
+ draft = ship.Draft
+
+ # We wanna intersect the whole ship with the free surface, so in this case
+ # we must not use the underwater side (or the tool will fail)
+ shape, _ = placeShipShape(ship.Shape.copy(), draft, roll, trim)
+
+ try:
+ f = Part.Face(shape.slice(Vector(0,0,1), 0.0))
+ area = Units.Quantity(f.Area, Units.Area)
+ except Part.OCCError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "Part.OCCError: Floating area cannot be computed",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
+ area = Units.Quantity(0.0, Units.Area)
bbox = shape.BoundBox
- xmin = bbox.XMin
- xmax = bbox.XMax
- ymin = bbox.YMin
- ymax = bbox.YMax
-
- # Create the "sea" box
- L = xmax - xmin
- B = ymax - ymin
- p = Vector(xmin - L, ymin - B, bbox.ZMin - 1.0)
+ Area = (bbox.XMax - bbox.XMin) * (bbox.YMax - bbox.YMin)
try:
- box = Part.makeBox(3.0 * L, 3.0 * B, - bbox.ZMin + 1.0, p)
- except Part.OCCError:
- return [area, cf]
+ cf = area.Value / Area
+ except ZeroDivisionError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "ZeroDivisionError: Null area found during the floating area"
+ " computation!",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
+ cf = 0.0
- maxX = bbox.XMin / Units.Metre.Value
- minX = bbox.XMax / Units.Metre.Value
- maxY = bbox.YMin / Units.Metre.Value
- minY = bbox.YMax / Units.Metre.Value
- for s in shape.Solids:
- try:
- common = box.common(s)
- except Part.OCCError:
- 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 (TypeError,Part.OCCError):
- 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 / Units.Metre.Value**2
- maxX = max(maxX, faceBounds.XMax / Units.Metre.Value)
- minX = min(minX, faceBounds.XMin / Units.Metre.Value)
- maxY = max(maxY, faceBounds.YMax / Units.Metre.Value)
- minY = min(minY, faceBounds.YMin / Units.Metre.Value)
- 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]
+ 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].
+def BMT(ship, draft=None, trim=Units.parseQuantity("0 deg")):
+ """Calculate "ship Bouyance center" - "transversal metacenter" radius
+
+ Position arguments:
+ ship -- Ship object (see createShip)
+
+ Keyword arguments:
+ draft -- Ship draft (Design ship draft by default)
+ trim -- Trim angle (0 degrees by default)
+
+ Returned value:
+ BMT radius
"""
+ if draft is None:
+ draft = ship.Draft
+
+ roll = Units.parseQuantity("0 deg")
+ _, B0, _ = displacement(ship, draft, roll, trim)
+
+
nRoll = 2
- maxRoll = 7.0
- B0 = displacement(ship, draft, 0.0, trim, 0.0)[1]
+ maxRoll = Units.parseQuantity("7 deg")
+
BM = 0.0
for i in range(nRoll):
- roll = (maxRoll / nRoll)*(i + 1)
- B1 = displacement(ship, draft, roll, trim, 0.0)[1]
+ roll = (maxRoll / nRoll) * (i + 1)
+ _, B1, _ = displacement(ship, draft, roll, trim)
# * 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
+ BB = B1 - B0
+ BB.x = 0.0
+ # nRoll is actually representing the weight function
+ BM += 0.5 * BB.Length / math.sin(math.radians(0.5 * roll)) / nRoll
+ return Units.Quantity(BM, Units.Length)
-def mainFrameCoeff(ship, draft):
- """ Calculate main frame coefficient.
- @param ship Selected ship instance
- @param draft Draft.
- @return Main frame coefficient
+def mainFrameCoeff(ship, draft=None):
+ """Compute the main frame coefficient
+
+ Position arguments:
+ ship -- Ship object (see createShip)
+
+ Keyword arguments:
+ draft -- Ship draft (Design ship draft by default)
+
+ Returned value:
+ Ship main frame area coefficient
"""
- cm = 0.0
- maxY = 0.0
- minY = 0.0
+ if draft is None:
+ draft = ship.Draft
- shape = ship.Shape.copy()
- shape.translate(Vector(0.0, 0.0, -draft * Units.Metre.Value))
- x = 0.0
- area = 0.0
+ shape, _ = placeShipShape(ship.Shape.copy(), draft,
+ Units.parseQuantity("0 deg"),
+ Units.parseQuantity("0 deg"))
+ shape = getUnderwaterSide(shape)
+
+ try:
+ f = Part.Face(shape.slice(Vector(1,0,0), 0.0))
+ area = f.Area
+ except Part.OCCError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "Part.OCCError: Main frame area cannot be computed",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
+ area = 0.0
bbox = shape.BoundBox
- xmin = bbox.XMin
- xmax = bbox.XMax
- ymin = bbox.YMin
- ymax = bbox.YMax
+ Area = (bbox.YMax - bbox.YMin) * (bbox.ZMax - bbox.ZMin)
- # Create the "sea" box
- L = xmax - xmin
- B = ymax - ymin
- p = Vector(xmin - L, ymin - B, bbox.ZMin - 1.0)
try:
- box = Part.makeBox(1.5 * L, 3.0 * B, - bbox.ZMin + 1.0, p)
- except Part.OCCError:
- return cm
+ cm = area / Area
+ except ZeroDivisionError:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "ZeroDivisionError: Null area found during the main frame area"
+ " coefficient computation!",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + '\n')
+ cm = 0.0
- maxY = bbox.YMin / Units.Metre.Value
- minY = bbox.YMax / Units.Metre.Value
- for s in shape.Solids:
- try:
- common = box.common(s)
- except Part.OCCError:
- 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 (TypeError,Part.OCCError):
- 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 / Units.Metre.Value**2
- maxY = max(maxY, faceBounds.YMax / Units.Metre.Value)
- minY = min(minY, faceBounds.YMin / Units.Metre.Value)
- 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.
+ """Hydrostatics point, that contains the following members:
+
+ draft -- Ship draft
+ trim -- Ship trim
+ disp -- Ship displacement
+ xcb -- Bouyance center X coordinate
+ wet -- Wetted ship area
+ mom -- Triming 1cm ship moment
+ farea -- Floating area
+ KBt -- Transversal KB height
+ BMt -- Transversal BM height
+ Cb -- Block coefficient.
+ Cf -- Floating coefficient.
+ Cm -- Main frame coefficient.
+
+ The moment to trim the ship 1 cm is positive when is resulting in a positive
+ trim angle.
"""
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.
+ """Compute all the hydrostatics.
+
+ Position argument:
+ ship -- Ship instance
+ faces -- Ship external faces
+ draft -- Ship draft
+ trim -- Trim angle
"""
- # Hydrostatics computation
- dispData = displacement(ship, draft, 0.0, trim, 0.0)
+ disp, B, cb = displacement(ship, draft=draft, trim=trim)
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)
+ wet = wettedArea(faces, draft=draft, trim=trim)
+ mom = moment(ship, draft=draft, trim=trim)
+ farea, cf = floatingArea(ship, draft=draft, trim=trim)
+ bm = BMT(ship, draft=draft, trim=trim)
+ cm = mainFrameCoeff(ship, draft=draft)
# Store final data
self.draft = draft
self.trim = trim
- self.disp = dispData[0]
- self.xcb = dispData[1].x
+ self.disp = disp
+ self.xcb = Units.Quantity(B.x, Units.Length)
self.wet = wet
- self.farea = farea[0]
+ self.farea = farea
self.mom = mom
- self.KBt = dispData[1].z
+ self.KBt = Units.Quantity(B.z, Units.Length)
self.BMt = bm
- self.Cb = dispData[2]
- self.Cf = farea[1]
- self.Cm = cm
+ self.Cb = cb
+ self.Cf = cf
+ self.Cm = cm
\ No newline at end of file