diff --git a/src/Mod/Ship/shipCapacityCurve/TaskPanel.py b/src/Mod/Ship/shipCapacityCurve/TaskPanel.py index 5f210b3a8..c4c61ab90 100644 --- a/src/Mod/Ship/shipCapacityCurve/TaskPanel.py +++ b/src/Mod/Ship/shipCapacityCurve/TaskPanel.py @@ -21,108 +21,31 @@ #* * #*************************************************************************** - import math import FreeCAD as App import FreeCADGui as Gui -from FreeCAD import Base, Vector -import Part import Units from PySide import QtGui, QtCore import PlotAux -import Instance +import TankInstance as Instance from shipUtils import Paths import shipUtils.Units as USys -import shipUtils.Locale as Locale -import Tools class TaskPanel: def __init__(self): - self.ui = Paths.modulePath() + "/shipHydrostatics/TaskPanel.ui" - self.ship = None - self.running = False + self.ui = Paths.modulePath() + "/shipCapacityCurve/TaskPanel.ui" + self.tank = None def accept(self): - if not self.ship: + if self.tank is None: return False - if self.running: - return - self.save() - - mw = self.getMainWindow() - form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.trim = self.widget(QtGui.QLineEdit, "Trim") - form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") - 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 - n_draft = form.nDraft.value() - - draft = min_draft - drafts = [draft] - dDraft = (max_draft - min_draft) / (n_draft - 1) - for i in range(1, n_draft): - draft = draft + dDraft - drafts.append(draft) - - # Compute data - # Get external faces - self.loop = QtCore.QEventLoop() - self.timer = QtCore.QTimer() - self.timer.setSingleShot(True) - QtCore.QObject.connect(self.timer, - QtCore.SIGNAL("timeout()"), - self.loop, - QtCore.SLOT("quit()")) - self.running = True - faces = self.externalFaces(self.ship.Shape) - if not self.running: - return False - if len(faces) == 0: - msg = QtGui.QApplication.translate( - "ship_console", - "Failure detecting external faces from the ship object", - None, - QtGui.QApplication.UnicodeUTF8) - App.Console.PrintError(msg + '\n') - return False - faces = Part.makeShell(faces) - # Get the hydrostatics - msg = QtGui.QApplication.translate( - "ship_console", - "Computing hydrostatics", - None, - QtGui.QApplication.UnicodeUTF8) - App.Console.PrintMessage(msg + '...\n') - points = [] - for i in range(len(drafts)): - App.Console.PrintMessage("\t{} / {}\n".format(i + 1, len(drafts))) - draft = drafts[i] - point = Tools.Point(self.ship, - faces, - draft, - trim) - points.append(point) - self.timer.start(0.0) - self.loop.exec_() - if(not self.running): - break - PlotAux.Plot(self.ship, trim, points) + # Plot data + l, z, v = self.compute() + PlotAux.Plot(l, z, v, self.tank) return True def reject(self): - if not self.ship: - return False - if self.running: - self.running = False - return return True def clicked(self, index): @@ -149,32 +72,18 @@ class TaskPanel: def setupUi(self): mw = self.getMainWindow() form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.trim = self.widget(QtGui.QLineEdit, "Trim") - form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") - form.maxDraft = self.widget(QtGui.QLineEdit, "MaxDraft") - form.nDraft = self.widget(QtGui.QSpinBox, "NDraft") + form.points = self.widget(QtGui.QSpinBox, "Points") 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): toplevel = QtGui.qApp.topLevelWidgets() for i in toplevel: if i.metaObject().className() == "Gui::MainWindow": return i - raise RuntimeError("No main window found") + raise Exception("No main window found") def widget(self, class_id, name): """Return the selected widget. @@ -190,379 +99,89 @@ class TaskPanel: def initValues(self): """ Set initial values for fields """ - mw = self.getMainWindow() - form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.trim = self.widget(QtGui.QLineEdit, "Trim") - form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") - form.maxDraft = self.widget(QtGui.QLineEdit, "MaxDraft") - form.nDraft = self.widget(QtGui.QSpinBox, "NDraft") - selObjs = Gui.Selection.getSelection() if not selObjs: msg = QtGui.QApplication.translate( "ship_console", - "A ship instance must be selected before using this tool (no" + "A tank instance must be selected before using this tool (no" " objects selected)", None, QtGui.QApplication.UnicodeUTF8) App.Console.PrintError(msg + '\n') return True - for i in range(len(selObjs)): + for i in range(0, len(selObjs)): obj = selObjs[i] props = obj.PropertiesList try: - props.index("IsShip") + props.index("IsTank") except ValueError: continue - if obj.IsShip: - if self.ship: + if obj.IsTank: + if self.tank: msg = QtGui.QApplication.translate( "ship_console", - "More than one ship have been selected (the extra" - " ships will be ignored)", + "More than one tank have been selected (the extra" + " tanks will be ignored)", None, QtGui.QApplication.UnicodeUTF8) App.Console.PrintWarning(msg + '\n') break - self.ship = obj - - if not self.ship: + self.tank = obj + if not self.tank: msg = QtGui.QApplication.translate( "ship_console", - "A ship instance must be selected before using this tool (no" - " valid ship found at the selected objects)", + "A tank instance must be selected before using this tool (no" + " valid tank found at the selected objects)", None, QtGui.QApplication.UnicodeUTF8) App.Console.PrintError(msg + '\n') return True - - props = self.ship.PropertiesList - - length_format = USys.getLengthFormat() - angle_format = USys.getAngleFormat() - - try: - props.index("HydrostaticsTrim") - form.trim.setText(Locale.toString(angle_format.format( - self.ship.HydrostaticsTrim.getValueAs( - USys.getLengthUnits()).Value))) - except ValueError: - form.trim.setText(Locale.toString(angle_format.format(0.0))) - - try: - props.index("HydrostaticsMinDraft") - form.minDraft.setText(Locale.toString(length_format.format( - self.ship.HydrostaticsMinDraft.getValueAs( - USys.getLengthUnits()).Value))) - except ValueError: - form.minDraft.setText(Locale.toString(length_format.format( - 0.9 * self.ship.Draft.getValueAs(USys.getLengthUnits()).Value))) - try: - props.index("HydrostaticsMaxDraft") - form.maxDraft.setText(Locale.toString(length_format.format( - self.ship.HydrostaticsMaxDraft.getValueAs( - USys.getLengthUnits()).Value))) - except ValueError: - form.maxDraft.setText(Locale.toString(length_format.format( - 1.1 * self.ship.Draft.getValueAs(USys.getLengthUnits()).Value))) - - try: - props.index("HydrostaticsNDraft") - form.nDraft.setValue(self.ship.HydrostaticsNDraft) - except ValueError: - pass - return False def retranslateUi(self): - """ Set user interface locale strings. - """ + """ Set user interface locale strings. """ mw = self.getMainWindow() form = mw.findChild(QtGui.QWidget, "TaskPanel") form.setWindowTitle(QtGui.QApplication.translate( - "ship_hydrostatic", - "Plot hydrostatics", + "ship_capacity", + "Plot the tank capacity curve", None, QtGui.QApplication.UnicodeUTF8)) - self.widget(QtGui.QLabel, "TrimLabel").setText( + self.widget(QtGui.QLabel, "PointsLabel").setText( QtGui.QApplication.translate( - "ship_hydrostatic", - "Trim", - None, - QtGui.QApplication.UnicodeUTF8)) - self.widget(QtGui.QLabel, "MinDraftLabel").setText( - QtGui.QApplication.translate( - "ship_hydrostatic", - "Minimum draft", - None, - QtGui.QApplication.UnicodeUTF8)) - self.widget(QtGui.QLabel, "MaxDraftLabel").setText( - QtGui.QApplication.translate( - "ship_hydrostatic", - "Maximum draft", - None, - QtGui.QApplication.UnicodeUTF8)) - self.widget(QtGui.QLabel, "NDraftLabel").setText( - QtGui.QApplication.translate( - "ship_hydrostatic", + "ship_capacity", "Number of points", None, QtGui.QApplication.UnicodeUTF8)) - def clampLength(self, widget, val_min, val_max, val): - if val >= val_min and 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))) - return val - - def onData(self, value): - """ Method called when input data is changed. - @param value Changed value. - """ - if not self.ship: - return + def compute(self): mw = self.getMainWindow() form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.trim = self.widget(QtGui.QLineEdit, "Trim") - form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") - form.maxDraft = self.widget(QtGui.QLineEdit, "MaxDraft") + form.points = self.widget(QtGui.QSpinBox, "Points") - # Get the values (or fix them in bad setting case) - try: - trim = Units.Quantity(Locale.fromString( - form.trim.text())).getValueAs('deg').Value - except: - trim = 0.0 - input_format = USys.getAngleFormat() - qty = Units.Quantity('{} deg'.format(trim)) - form.trim.setText(Locale.toString(input_format.format( - qty.getValueAs(USys.getLengthUnits()).Value))) - try: - min_draft = Units.Quantity(Locale.fromString( - form.minDraft.text())).getValueAs('m').Value - except: - min_draft = 0.9 * self.ship.Draft.getValueAs('m').Value - input_format = USys.getLengthFormat() - qty = Units.Quantity('{} m'.format(min_draft)) - form.minDraft.setText(Locale.toString(input_format.format( - qty.getValueAs(USys.getLengthUnits()).Value))) - try: - max_draft = Units.Quantity(Locale.fromString( - form.minDraft.text())).getValueAs('m').Value - except: - max_draft = 0.9 * self.ship.Draft.getValueAs('m').Value - input_format = USys.getLengthFormat() - qty = Units.Quantity('{} m'.format(max_draft)) - form.maxDraft.setText(Locale.toString(input_format.format( - qty.getValueAs(USys.getLengthUnits()).Value))) + bbox = self.tank.Shape.BoundBox + dz = Units.Quantity(bbox.ZMax - bbox.ZMin, Units.Length) - # Clamp the values to the bounds - bbox = self.ship.Shape.BoundBox - draft_min = bbox.ZMin / Units.Metre.Value - draft_max = bbox.ZMax / Units.Metre.Value - min_draft = self.clampLength(form.minDraft, - draft_min, - draft_max, - min_draft) - max_draft = self.clampLength(form.maxDraft, - draft_min, - draft_max, - max_draft) - trim_min = -180.0 - trim_max = 180.0 - trim = self.clampAngle(form.trim, trim_min, trim_max, trim) + n = form.points.value() + dlevel = 100.0 / (n - 1) + l = [0.0] + v = [0.0] + z = [0.0] - # Clamp draft values to assert that the minimum value is lower than - # the maximum one - min_draft = self.clampLength(form.minDraft, - draft_min, - max_draft, - min_draft) - max_draft = self.clampLength(form.maxDraft, - min_draft, - draft_max, - max_draft) - - - def save(self): - """ Saves data into ship instance. - """ - mw = self.getMainWindow() - form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.trim = self.widget(QtGui.QLineEdit, "Trim") - form.minDraft = self.widget(QtGui.QLineEdit, "MinDraft") - 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 - n_draft = form.nDraft.value() - - props = self.ship.PropertiesList - try: - props.index("HydrostaticsTrim") - except ValueError: - tooltip = str(QtGui.QApplication.translate( - "ship_hydrostatic", - "Hydrostatics tool trim selected", - None, - QtGui.QApplication.UnicodeUTF8)) - self.ship.addProperty("App::PropertyAngle", - "HydrostaticsTrim", - "Ship", - tooltip) - self.ship.HydrostaticsTrim = '{} deg'.format(trim) - - try: - props.index("HydrostaticsMinDraft") - except ValueError: - tooltip = str(QtGui.QApplication.translate( - "ship_hydrostatic", - "Hydrostatics tool minimum draft selected [m]", - None, - QtGui.QApplication.UnicodeUTF8)) - self.ship.addProperty("App::PropertyLength", - "HydrostaticsMinDraft", - "Ship", - tooltip) - self.ship.HydrostaticsMinDraft = '{} m'.format(min_draft) - - try: - props.index("HydrostaticsMaxDraft") - except ValueError: - tooltip = str(QtGui.QApplication.translate( - "ship_hydrostatic", - "Hydrostatics tool maximum draft selected [m]", - None, - QtGui.QApplication.UnicodeUTF8)) - self.ship.addProperty("App::PropertyLength", - "HydrostaticsMaxDraft", - "Ship", - tooltip) - self.ship.HydrostaticsMaxDraft = '{} m'.format(max_draft) - - try: - props.index("HydrostaticsNDraft") - except ValueError: - tooltip = str(QtGui.QApplication.translate( - "ship_hydrostatic", - "Hydrostatics tool number of points selected", - None, - QtGui.QApplication.UnicodeUTF8)) - self.ship.addProperty("App::PropertyInteger", - "HydrostaticsNDraft", - "Ship", - tooltip) - self.ship.HydrostaticsNDraft = form.nDraft.value() - - def lineFaceSection(self, line, surface): - """ Returns the point of section of a line with a face - @param line Line object, that can be a curve. - @param surface Surface object (must be a Part::Shape) - @return Section points array, [] if line don't cut surface - """ - result = [] - vertexes = line.Vertexes - nVertex = len(vertexes) - - section = line.cut(surface) - - points = section.Vertexes - return points - - def externalFaces(self, shape): - """ Returns detected external faces. - @param shape Shape where external faces wanted. - @return List of external faces detected. - """ - result = [] - faces = shape.Faces - bbox = shape.BoundBox - L = bbox.XMax - bbox.XMin - B = bbox.YMax - bbox.YMin - T = bbox.ZMax - bbox.ZMin - dist = math.sqrt(L*L + B*B + T*T) msg = QtGui.QApplication.translate( "ship_console", - "Computing external faces", + "Computing capacity curves", None, QtGui.QApplication.UnicodeUTF8) App.Console.PrintMessage(msg + '...\n') - # Valid/unvalid faces detection loop - for i in range(len(faces)): - App.Console.PrintMessage("\t{} / {}\n".format(i + 1, len(faces))) - f = faces[i] - # Create a line normal to surface at middle point - u = 0.0 - v = 0.0 - try: - surf = f.Surface - u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1]) - v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1]) - except: - cog = f.CenterOfMass - [u, v] = f.Surface.parameter(cog) - p0 = f.valueAt(u, v) - try: - n = f.normalAt(u, v).normalize() - except: - continue - p1 = p0 + n.multiply(1.5 * dist) - line = Part.makeLine(p0, p1) - # Look for faces in front of this - nPoints = 0 - for j in range(len(faces)): - f2 = faces[j] - section = self.lineFaceSection(line, f2) - if len(section) <= 2: - continue - # Add points discarding start and end - nPoints = nPoints + len(section) - 2 - # In order to avoid special directions we can modify line - # normal a little bit. - angle = 5 - line.rotate(p0, Vector(1, 0, 0), angle) - line.rotate(p0, Vector(0, 1, 0), angle) - line.rotate(p0, Vector(0, 0, 1), angle) - nPoints2 = 0 - for j in range(len(faces)): - if i == j: - continue - f2 = faces[j] - section = self.lineFaceSection(line, f2) - if len(section) <= 2: - continue - # Add points discarding start and end - nPoints2 = nPoints + len(section) - 2 - # If the number of intersection points is pair, is a - # external face. So if we found an odd points intersection, - # face must be discarded. - if (nPoints % 2) or (nPoints2 % 2): - continue - result.append(f) - self.timer.start(0.0) - self.loop.exec_() - if(not self.running): - break - return result + for i in range(1, n): + App.Console.PrintMessage("\t{} / {}\n".format(i + 1, n)) + level = i * dlevel + vol = self.tank.Proxy.setFillingLevel(self.tank, level) + l.append(level) + z.append(level / 100.0 * dz.getValueAs("m").Value) + v.append(vol.getValueAs("m^3").Value) + return (l, z, v) def createTask():