diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt
index 10f63449c1..efbc08b584 100644
--- a/src/Mod/Ship/CMakeLists.txt
+++ b/src/Mod/Ship/CMakeLists.txt
@@ -90,6 +90,7 @@ SET(ShipGZ_SRCS
shipGZ/PlotAux.py
shipGZ/TaskPanel.py
shipGZ/TaskPanel.ui
+ shipGZ/Tools.py
)
SOURCE_GROUP("shipgz" FILES ${ShipGZ_SRCS})
diff --git a/src/Mod/Ship/shipGZ/TaskPanel.py b/src/Mod/Ship/shipGZ/TaskPanel.py
index f54d7d428e..ccc87cdfc4 100644
--- a/src/Mod/Ship/shipGZ/TaskPanel.py
+++ b/src/Mod/Ship/shipGZ/TaskPanel.py
@@ -27,6 +27,7 @@ import FreeCADGui as Gui
import Units
from PySide import QtGui, QtCore
import PlotAux
+import Tools
from shipUtils import Paths
import shipUtils.Units as USys
import shipUtils.Locale as Locale
@@ -40,6 +41,26 @@ class TaskPanel:
if self.lc is None:
return False
self.save()
+
+ mw = self.getMainWindow()
+ form = mw.findChild(QtGui.QWidget, "TaskPanel")
+ form.angle = self.widget(QtGui.QLineEdit, "Angle")
+ form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
+ form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
+
+ rolls = []
+ roll = Units.Quantity(Locale.fromString(
+ form.angle.text())).getValueAs('deg').Value
+ n_points = form.n_points.value()
+ for i in range(n_points):
+ rolls.append(roll * i / float(n_points - 1))
+
+ gz = Tools.solve(self.ship,
+ self.weights,
+ self.tanks
+ rolls,
+ form.var_trim.isChecked())
+
return True
def reject(self):
@@ -72,7 +93,6 @@ class TaskPanel:
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
- form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
self.form = form
if self.initValues():
@@ -278,7 +298,6 @@ class TaskPanel:
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
- form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
form.angle.setText(Locale.toString(angle_format.format(90.0)))
# Try to use saved values
@@ -295,14 +314,6 @@ class TaskPanel:
form.n_points.setValue(self.ship.GZNumPoints)
except ValueError:
pass
- try:
- props.index("GZVariableDraft")
- if self.ship.GZVariableDraft:
- form.var_draft.setCheckState(QtCore.Qt.Checked)
- else:
- form.var_draft.setCheckState(QtCore.Qt.Unchecked)
- except ValueError:
- pass
try:
props.index("GZVariableTrim")
if self.ship.GZVariableTrim:
@@ -336,20 +347,6 @@ class TaskPanel:
"Number of points",
None,
QtGui.QApplication.UnicodeUTF8))
- self.widget(QtGui.QCheckBox, "VariableDraft").setText(
- QtGui.QApplication.translate(
- "ship_gz",
- "Variable draft",
- None,
- QtGui.QApplication.UnicodeUTF8))
- self.widget(QtGui.QCheckBox, "VariableDraft").setToolTip(
- QtGui.QApplication.translate(
- "ship_gz",
- "The ship will be moved to the equilibrium draft for each" + \
- " roll angle. It will significantly increase the required" + \
- " computing time",
- None,
- QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QCheckBox, "VariableTrim").setText(
QtGui.QApplication.translate(
"ship_gz",
@@ -371,13 +368,11 @@ class TaskPanel:
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
- form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
angle = Units.Quantity(Locale.fromString(
form.angle.text())).getValueAs('deg').Value
n_points = form.n_points.value()
- var_draft = form.var_draft.isChecked()
var_trim = form.var_trim.isChecked()
props = self.ship.PropertiesList
@@ -413,22 +408,6 @@ class TaskPanel:
"Ship",
tooltip)
self.ship.GZNumPoints = n_points
- try:
- props.index("GZVariableDraft")
- except ValueError:
- try:
- tooltip = str(QtGui.QApplication.translate(
- "ship_areas",
- "GZ curve tool variable draft selection",
- None,
- QtGui.QApplication.UnicodeUTF8))
- except:
- tooltip = "GZ curve tool variable draft selection"
- self.ship.addProperty("App::PropertyBool",
- "GZVariableDraft",
- "Ship",
- tooltip)
- self.ship.GZVariableDraft = var_draft
try:
props.index("GZVariableTrim")
except ValueError:
diff --git a/src/Mod/Ship/shipGZ/TaskPanel.ui b/src/Mod/Ship/shipGZ/TaskPanel.ui
index 36fa605a32..b57157e5f0 100644
--- a/src/Mod/Ship/shipGZ/TaskPanel.ui
+++ b/src/Mod/Ship/shipGZ/TaskPanel.ui
@@ -33,16 +33,6 @@
- -
-
-
- Variable Draft
-
-
- true
-
-
-
-
@@ -59,7 +49,7 @@
-
- -
+
-
Variable Trim angle
diff --git a/src/Mod/Ship/shipGZ/Tools.py b/src/Mod/Ship/shipGZ/Tools.py
new file mode 100644
index 0000000000..b025a1800d
--- /dev/null
+++ b/src/Mod/Ship/shipGZ/Tools.py
@@ -0,0 +1,143 @@
+#***************************************************************************
+#* *
+#* Copyright (c) 2011, 2012 *
+#* Jose Luis Cercos Pita *
+#* *
+#* This program is free software; you can redistribute it and/or modify *
+#* it under the terms of the GNU Lesser General Public License (LGPL) *
+#* as published by the Free Software Foundation; either version 2 of *
+#* the License, or (at your option) any later version. *
+#* for detail see the LICENCE text file. *
+#* *
+#* This program is distributed in the hope that it will be useful, *
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+#* GNU Library General Public License for more details. *
+#* *
+#* You should have received a copy of the GNU Library General Public *
+#* 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
+from FreeCAD import Vector, Matrix, Placement
+import Part
+import Units
+import FreeCAD as App
+import FreeCADGui as Gui
+import Instance as ShipInstance
+import WeightInstance
+import TankInstance
+from shipHydrostatics import Tools as Hydrostatics
+
+
+G = 9.81
+MAX_EQUILIBRIUM_ITERS = 10
+DENS = 1.025 # [tons/m3], salt water
+
+
+def solve(ship, weights, tanks, rolls, var_trim=True):
+ """ Compute the ship GZ curve.
+ @param ship Ship instance.
+ @param weights Considered weights.
+ @param tanks Considered tanks.
+ @param rolls List of considered roll angles.
+ @param var_trim True if the trim angle should be recomputed at each roll
+ angle, False otherwise.
+ @return GZ values for each roll angle
+ """
+ # Get the unloaded weight (ignoring the tanks for the moment).
+ W = 0.0
+ COG = Vector()
+ for w in weights:
+ W += w.Proxy.getMass(w).getValueAs('kg').Value
+ m = w.Proxy.getMoment(w)
+ COG.x += m[0].getValueAs('kg*m').Value
+ COG.y += m[1].getValueAs('kg*m').Value
+ COG.z += m[2].getValueAs('kg*m').Value
+ COG = COG.multiply(1.0 / W)
+ W = W * G
+
+ # Get the tanks weight
+ TW = 0.0
+ for t in tanks:
+ # t[0] = tank object
+ # t[1] = load density
+ # t[2] = filling level
+ vol = t[0].Proxy.setFillingLevel(t[0], t[2]).getValueAs('m^3').Value
+ TW += vol * t[1]
+ TW = TW.getValueAs('kg').Value * G
+
+ gzs = []
+ for roll in rolls:
+ gz = solve_point(W, COG, TW, ship, tanks, roll, var_trim)
+ if gz is None:
+ return []
+ gzs.append(solve_point(W, COG, TW, ship, tanks, roll, var_trim))
+
+ return gzs
+
+def solve_point(W, COG, TW, ship, tanks, roll, var_trim=True):
+ """ Compute the ship GZ value.
+ @param W Empty ship weight.
+ @param COG Empty ship Center of mass.
+ @param tanks Considered tanks.
+ @param roll Roll angle.
+ @param var_trim True if the trim angle should be recomputed at each roll
+ angle, False otherwise.
+ @return GZ value
+ """
+ gz = 0.0
+
+ # Look for the equilibrium draft (and eventually the trim angle too)
+ max_draft = ship.Shape.BoundBox.ZMax
+ draft = max_draft
+ max_disp = ship.Shape.Volume.getValueAs('m^3').Value * DENS * 1000.0 * G
+ if max_disp < W + TW:
+ msg = QtGui.QApplication.translate(
+ "ship_console",
+ "Too much weight! The ship will never displace water enough",
+ None,
+ QtGui.QApplication.UnicodeUTF8)
+ App.Console.PrintError(msg + ' ({} tons vs. {} tons)\n'.format(
+ max_disp / 1000.0 / G, (W + TW) / 1000.0 / G))
+ return None
+ trim = 0.0
+ 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
+ # Get the empty ship weight transformed application point
+ p = Part.makePoint(COG)
+ p.translate(Vector(0.0, 0.0, -draft))
+ m = Matrix()
+ m.rotateX(math.radians(roll))
+ m.rotateY(-math.radians(trim))
+ p.rotate(Placement(m))
+ # Add the tanks
+ # TODO
+ # ---
+
+ # Compute the errors
+ draft_error = abs(disp - W - TW) / max_disp
+ if not var_trim:
+ trim_error = 0.0
+ else:
+ dx = B.x - p.X
+ dz = B.z - p.Z
+ if abs(dx) < 0.001 * ship.Length.getValueAs('m').Value:
+ trim_error = 0.0
+ else:
+ trim_error = math.degrees(math.atan2(dz, dx))
+
+ # Check if we can tolerate the errors
+ if draft_error < 0.01 and trim_error < 1.0:
+ break
+
+ # Get the new draft and trim
+ draft += draft_error * max_draft
+ trim += 0.5 * trim_error
+
+ return B.y - p.Y
\ No newline at end of file