diff --git a/src/Mod/Ship/Instance.py b/src/Mod/Ship/Instance.py index 96045141d..27cb36056 100644 --- a/src/Mod/Ship/Instance.py +++ b/src/Mod/Ship/Instance.py @@ -22,6 +22,7 @@ #*************************************************************************** import time +from math import * # COIN from pivy.coin import * @@ -48,6 +49,7 @@ class Ship: obj.addProperty("App::PropertyLength","Draft","Ship", str(Translator.translate("Ship draft (T) [m]"))).Draft=0.0 # Add shapes obj.Shape = Part.makeCompound(solids) + obj.addProperty("Part::PropertyPartShape","ExternalFaces","Ship", str(Translator.translate("Ship only external faces"))) obj.Proxy = self def onChanged(self, fp, prop): @@ -64,37 +66,6 @@ class Ship: """ fp.Shape = Part.makeCompound(fp.Shape.Solids) - def lineFaceSection(self,line,surface): - """ Returns the point of section of a line with a face - @param line Line object, that can be a curve. - @param surface Surface object (must be a Part::Shape) - @return Section points array, [] if line don't cut surface - """ - # Get initial data - result = [] - vertexes = line.Vertexes - nVertex = len(vertexes) - # Perform the cut - section = line.cut(surface) - # Filter all old points - points = section.Vertexes - nPoint = len(points) - if nPoint <= nVertex: - # Any valid point - return result - for i in range(0,nPoint): - disp = len(result) - flag = 0 - if not Math.isAprox(points[i].X,vertexes[i-disp].X,0.0001): - flag = flag+1 - if not Math.isAprox(points[i].Y,vertexes[i-disp].Y,0.0001): - flag = flag+1 - if not Math.isAprox(points[i].Z,vertexes[i-disp].Z,0.0001): - flag = flag+1 - if flag > 0: - result.append(points[i]) - return result - class ViewProviderShip: def __init__(self, obj): "Set this object to the proxy object of the actual view provider" diff --git a/src/Mod/Ship/TankInstance.py b/src/Mod/Ship/TankInstance.py index 6e44795f8..19288ffe8 100644 --- a/src/Mod/Ship/TankInstance.py +++ b/src/Mod/Ship/TankInstance.py @@ -52,7 +52,6 @@ class ShipTank: if not shape: obj.IsShipTank=False return - # obj.addProperty("Part::PropertyPartShape","Shape","ShipTank", str(Translator.translate("Tank solid"))).Shape = shape obj.Shape = shape obj.Proxy = self self.obj = obj diff --git a/src/Mod/Ship/shipHydrostatics/Plot.py b/src/Mod/Ship/shipHydrostatics/Plot.py index 0d3fdf93e..d953b5ddf 100644 --- a/src/Mod/Ship/shipHydrostatics/Plot.py +++ b/src/Mod/Ship/shipHydrostatics/Plot.py @@ -22,9 +22,10 @@ #*************************************************************************** import os +import math # FreeCAD modules import FreeCAD,FreeCADGui -from FreeCAD import Part, Base +from FreeCAD import Part, Base, Vector from FreeCAD import Image, ImageGui # FreeCADShip modules from shipUtils import Paths, Translator @@ -107,11 +108,19 @@ class Plot(object): Output.write(" # 11: Cm (Main frame coefficient)\n") Output.write(" #\n") Output.write(" #################################################################\n") + # Get external faces + faces = self.externalFaces(ship.Shape) + if len(faces) == 0: + msg = Translator.translate("Can't detect external faces from ship object.\n") + FreeCAD.Console.PrintError(msg) + else: + faces = Part.makeShell(faces) # Print data + FreeCAD.Console.PrintMessage("Computing hydrostatics...\n") for i in range(0,len(drafts)): - FreeCAD.Console.PrintMessage("%d / %d" % (i+1, len(drafts))) + FreeCAD.Console.PrintMessage("\t%d / %d\n" % (i+1, len(drafts))) draft = drafts[i] - point = Tools.Point(ship,draft,trim) + point = Tools.Point(ship,faces,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 @@ -251,3 +260,86 @@ class Plot(object): FreeCAD.Console.PrintError(msg) return True return False + + def lineFaceSection(self,line,surface): + """ Returns the point of section of a line with a face + @param line Line object, that can be a curve. + @param surface Surface object (must be a Part::Shape) + @return Section points array, [] if line don't cut surface + """ + # Get initial data + result = [] + vertexes = line.Vertexes + nVertex = len(vertexes) + # Perform the cut + section = line.cut(surface) + # Filter all old points + points = section.Vertexes + return points + + 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) + FreeCAD.Console.PrintMessage("Computing external faces...\n") + # Valid/unvalid faces detection loop + for i in range(0,len(faces)): + FreeCAD.Console.PrintMessage("\t%d / %d\n" % (i+1, len(faces))) + f = faces[i] + # Create a line normal to surface at middle point + u = 0.0 + v = 0.0 + try: + surf = f.Surface + u = 0.5*(surf.getUKnots()[0]+surf.getUKnots()[-1]) + v = 0.5*(surf.getVKnots()[0]+surf.getVKnots()[-1]) + except: + cog = f.CenterOfMass + [u,v] = f.Surface.parameter(cog) + p0 = f.valueAt(u,v) + try: + n = f.normalAt(u,v).normalize() + except: + continue + p1 = p0 + n.multiply(1.5*dist) + line = Part.makeLine(p0, p1) + # Look for faces in front of this + nPoints = 0 + for j in range(0,len(faces)): + f2 = faces[j] + section = self.lineFaceSection(line, f2) + if len(section) <= 2: + continue + # Add points discarding start and end + nPoints = nPoints + len(section) - 2 + # In order to avoid special directions we can modify line + # normal a little bit. + angle = 5 + line.rotate(p0,Vector(1,0,0),angle) + line.rotate(p0,Vector(0,1,0),angle) + line.rotate(p0,Vector(0,0,1),angle) + nPoints2 = 0 + for j in range(0,len(faces)): + if i == j: + continue + f2 = faces[j] + section = self.lineFaceSection(line, f2) + if len(section) <= 2: + continue + # Add points discarding start and end + nPoints2 = nPoints + len(section) - 2 + # If the number of intersection points is pair, is a + # external face. So if we found an odd points intersection, + # face must be discarded. + if (nPoints % 2) or (nPoints2 % 2): + continue + result.append(f) + return result diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py index 7c58f9a0b..591df4603 100644 --- a/src/Mod/Ship/shipHydrostatics/Tools.py +++ b/src/Mod/Ship/shipHydrostatics/Tools.py @@ -175,19 +175,17 @@ def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0): dens = 1.025 # [tons/m3], salt water return [dens*vol, B, vol/Vol] -def wettedArea(ship, draft, trim): +def wettedArea(shape, draft, trim): """ Calculate wetted ship area. - @param ship Selected ship instance + @param shape Ship external faces instance. @param draft Draft. @param trim Trim in degrees. @return Wetted ship area. """ - return 0.0 - faces = [] area = 0.0 nObjects = 0 # We will take a duplicate of ship shape in order to place it - shape = ship.Shape.copy() + shape = shape.copy() shape.translate(Vector(0.0,0.0,-draft)) shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim) # Now we need to know the x range of values @@ -200,57 +198,13 @@ def wettedArea(ship, draft, trim): p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0) box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p) # Compute common part with ship - for s in shape.Solids: + for f in shape.Faces: # Get solids intersection try: - common = box.common(s) + common = box.common(f) except: continue - if common.Volume == 0.0: - continue - # Recompute object adding it to the scene, when we have - # computed desired data we can remove it. - try: - Part.show(common) - except: - continue - nObjects = nObjects + 1 - # Divide by faces and discard free surface faces - faces = common.Faces - for f in faces: - fbox = f.BoundBox - # Orientation filter - if (fbox.ZMax-fbox.ZMin > 0.00001) and (abs(fbox.ZMax) > 0.00001): - continue - # Valid face, append it - faces.append(f) - # Study repeated faces, if a face is repeated, both of them must be - # discarded. - i = 0; - while(i < len(faces)): - j = i+1 - while(j < len(faces)): - f0 = faces[i] - f1 = faces[j] - # We will consider same face if share all their vertexes - nVertex = len(f0.Vertexes) - for v0 in f0.Vertexes: - for v1 in f1.Vertexes: - if Math.isSameVertex(v0,v1): - nVertex = nVertex - 1 - if nVertex <= 0: - del faces[j] - del faces[i] - i = i-1 - break - j = j+1 - i = i+1 - # Integrate areas - for f in faces: - area = area + f.Area - # Destroy generated objects - for i in range(0,nObjects): - App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name) + area = area + common.Area return area def moment(ship, draft, trim, disp, xcb): @@ -435,16 +389,20 @@ class Point: Cm Main frame coefficient. @note Moment is positive when produce positive trim. """ - def __init__(self, ship, draft, trim): + def __init__(self, ship, faces, draft, trim): """ Use all hydrostatics tools to define a hydrostatics point. @param ship Selected ship instance + @param faces Ship external faces @param draft Draft. @param trim Trim in degrees. """ # Hydrostatics computation dispData = displacement(ship,draft,0.0,trim,0.0) - wet = wettedArea(ship,draft,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)