External faces detection implemented that allows to compute wetted area.
This commit is contained in:
parent
c524a0268a
commit
6c1158e2e9
|
@ -22,6 +22,7 @@
|
||||||
#***************************************************************************
|
#***************************************************************************
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
from math import *
|
||||||
|
|
||||||
# COIN
|
# COIN
|
||||||
from pivy.coin import *
|
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
|
obj.addProperty("App::PropertyLength","Draft","Ship", str(Translator.translate("Ship draft (T) [m]"))).Draft=0.0
|
||||||
# Add shapes
|
# Add shapes
|
||||||
obj.Shape = Part.makeCompound(solids)
|
obj.Shape = Part.makeCompound(solids)
|
||||||
|
obj.addProperty("Part::PropertyPartShape","ExternalFaces","Ship", str(Translator.translate("Ship only external faces")))
|
||||||
obj.Proxy = self
|
obj.Proxy = self
|
||||||
|
|
||||||
def onChanged(self, fp, prop):
|
def onChanged(self, fp, prop):
|
||||||
|
@ -64,37 +66,6 @@ class Ship:
|
||||||
"""
|
"""
|
||||||
fp.Shape = Part.makeCompound(fp.Shape.Solids)
|
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:
|
class ViewProviderShip:
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
"Set this object to the proxy object of the actual view provider"
|
"Set this object to the proxy object of the actual view provider"
|
||||||
|
|
|
@ -52,7 +52,6 @@ class ShipTank:
|
||||||
if not shape:
|
if not shape:
|
||||||
obj.IsShipTank=False
|
obj.IsShipTank=False
|
||||||
return
|
return
|
||||||
# obj.addProperty("Part::PropertyPartShape","Shape","ShipTank", str(Translator.translate("Tank solid"))).Shape = shape
|
|
||||||
obj.Shape = shape
|
obj.Shape = shape
|
||||||
obj.Proxy = self
|
obj.Proxy = self
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
#***************************************************************************
|
#***************************************************************************
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import math
|
||||||
# FreeCAD modules
|
# FreeCAD modules
|
||||||
import FreeCAD,FreeCADGui
|
import FreeCAD,FreeCADGui
|
||||||
from FreeCAD import Part, Base
|
from FreeCAD import Part, Base, Vector
|
||||||
from FreeCAD import Image, ImageGui
|
from FreeCAD import Image, ImageGui
|
||||||
# FreeCADShip modules
|
# FreeCADShip modules
|
||||||
from shipUtils import Paths, Translator
|
from shipUtils import Paths, Translator
|
||||||
|
@ -107,11 +108,19 @@ class Plot(object):
|
||||||
Output.write(" # 11: Cm (Main frame coefficient)\n")
|
Output.write(" # 11: Cm (Main frame coefficient)\n")
|
||||||
Output.write(" #\n")
|
Output.write(" #\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
|
# Print data
|
||||||
|
FreeCAD.Console.PrintMessage("Computing hydrostatics...\n")
|
||||||
for i in range(0,len(drafts)):
|
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]
|
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)
|
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)
|
Output.write(string)
|
||||||
# Close file
|
# Close file
|
||||||
|
@ -251,3 +260,86 @@ class Plot(object):
|
||||||
FreeCAD.Console.PrintError(msg)
|
FreeCAD.Console.PrintError(msg)
|
||||||
return True
|
return True
|
||||||
return False
|
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
|
||||||
|
|
|
@ -175,19 +175,17 @@ def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0):
|
||||||
dens = 1.025 # [tons/m3], salt water
|
dens = 1.025 # [tons/m3], salt water
|
||||||
return [dens*vol, B, vol/Vol]
|
return [dens*vol, B, vol/Vol]
|
||||||
|
|
||||||
def wettedArea(ship, draft, trim):
|
def wettedArea(shape, draft, trim):
|
||||||
""" Calculate wetted ship area.
|
""" Calculate wetted ship area.
|
||||||
@param ship Selected ship instance
|
@param shape Ship external faces instance.
|
||||||
@param draft Draft.
|
@param draft Draft.
|
||||||
@param trim Trim in degrees.
|
@param trim Trim in degrees.
|
||||||
@return Wetted ship area.
|
@return Wetted ship area.
|
||||||
"""
|
"""
|
||||||
return 0.0
|
|
||||||
faces = []
|
|
||||||
area = 0.0
|
area = 0.0
|
||||||
nObjects = 0
|
nObjects = 0
|
||||||
# We will take a duplicate of ship shape in order to place it
|
# 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.translate(Vector(0.0,0.0,-draft))
|
||||||
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
|
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
|
# 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)
|
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)
|
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
|
||||||
# Compute common part with ship
|
# Compute common part with ship
|
||||||
for s in shape.Solids:
|
for f in shape.Faces:
|
||||||
# Get solids intersection
|
# Get solids intersection
|
||||||
try:
|
try:
|
||||||
common = box.common(s)
|
common = box.common(f)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
if common.Volume == 0.0:
|
area = area + common.Area
|
||||||
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)
|
|
||||||
return area
|
return area
|
||||||
|
|
||||||
def moment(ship, draft, trim, disp, xcb):
|
def moment(ship, draft, trim, disp, xcb):
|
||||||
|
@ -435,16 +389,20 @@ class Point:
|
||||||
Cm Main frame coefficient.
|
Cm Main frame coefficient.
|
||||||
@note Moment is positive when produce positive trim.
|
@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
|
""" Use all hydrostatics tools to define a hydrostatics
|
||||||
point.
|
point.
|
||||||
@param ship Selected ship instance
|
@param ship Selected ship instance
|
||||||
|
@param faces Ship external faces
|
||||||
@param draft Draft.
|
@param draft Draft.
|
||||||
@param trim Trim in degrees.
|
@param trim Trim in degrees.
|
||||||
"""
|
"""
|
||||||
# Hydrostatics computation
|
# Hydrostatics computation
|
||||||
dispData = displacement(ship,draft,0.0,trim,0.0)
|
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)
|
mom = moment(ship,draft,trim,dispData[0],dispData[1].x)
|
||||||
farea = FloatingArea(ship,draft,trim)
|
farea = FloatingArea(ship,draft,trim)
|
||||||
bm = BMT(ship,draft,trim)
|
bm = BMT(ship,draft,trim)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user