Skip to content

Commit

Permalink
tkCADFix: add check watertight
Browse files Browse the repository at this point in the history
  • Loading branch information
benoit128 committed Nov 13, 2024
1 parent af2f85f commit dec548d
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 30 deletions.
8 changes: 4 additions & 4 deletions Cassiopee/CPlot/CPlot/PyTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,11 @@ def render():
CPlot.render()

#==============================================================================
# This function doesnt remove zone from t
def delete(zlist):
"""Delete zones from plotter.
Usage: delete([i1,i2,...])"""
CPlot.delete(zlist)

"""Delete zones from plotter."""
CPlot.delete(zlist)

#==============================================================================
def add(t, nob, noz, zone):
"""Add/insert a zone to plotter.
Expand Down
2 changes: 1 addition & 1 deletion Cassiopee/CPlot/apps/tkBlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def rmBlock():
deletedZoneNames.append(CTK.t[2][nob][0]+Internal.SEP1+CTK.t[2][nob][2][noz][0])

CTK.saveTree()
CTK.t = CPlot.deleteSelection(CTK.t, CTK.Nb, CTK.Nz, nzs)
CTK.t = CPlot.deleteSelection(CTK.t, CTK.Nb, CTK.Nz, nzs)

CTK.TXT.insert('START', 'Selected zones deleted.\n')
(CTK.Nb, CTK.Nz) = CPlot.updateCPlotNumbering(CTK.t)
Expand Down
85 changes: 75 additions & 10 deletions Cassiopee/CPlot/apps/tkCADFix.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,58 @@ def trimFaces(event=None):

CTK.TXT.insert('START', 'Faces trimmed in CAD.\n')

#==============================================================================
def checkWatertight(event=None):
if CTK.t == []: return
b = Internal.getNodeFromName1(CTK.t, 'FACES')
if b is None:
CTK.TXT.insert('START', 'No FACES base. Check not performed.\n')
return
import Transform.PyTree as T
import Post.PyTree as P

CTK.setCursor(2, WIDGETS['frame'])
CTK.setCursor(2, WIDGETS['checkWatertight'])

CTK.saveTree()
p = Internal.getNodesFromName1(CTK.t, 'LEAKS')
if p is not None:
zones = []
for z in Internal.getZones(p): zones.append('LEAKS'+Internal.SEP1+z[0])
CPlot.delete(zones)
gnob = C.getNobOfBase(p, CTK.t)
del CTK.t[2][gnob]

CTK.t = C.addBase2PyTree(CTK.t, 'LEAKS', 1)
p = Internal.getNodesFromName1(CTK.t, 'LEAKS')
gnob = C.getNobOfBase(p, CTK.t)

f = Internal.getZones(b)
f = T.join(f, tol=1.e-6)
ef = T.splitConnexity(f)
VARS[6].set('Components: %d'%(len(ef)))

isWatertight = False
try:
ext = P.exteriorFaces(f)
ext = T.splitConnexity(ext)
for i in ext: CTK.add(CTK.t, gnob, -1, i)
if len(ext) == 0: isWatertight = True
except: isWatertight = True

(CTK.Nb, CTK.Nz) = CPlot.updateCPlotNumbering(CTK.t)
CTK.TKTREE.updateApp()
CTK.display(CTK.t)

CTK.setCursor(0, WIDGETS['frame'])
CTK.setCursor(0, WIDGETS['checkWatertight'])

if isWatertight: TTK.setButtonGreen(WIDGETS['checkWatertight'])
else: TTK.setButtonRed(WIDGETS['checkWatertight'])

if isWatertight: CTK.TXT.insert('START', 'CAD is watertight.\n')
else: CTK.TXT.insert('START', 'CAD is not watertight.\n')

#==============================================================================
# Create app widgets
#==============================================================================
Expand Down Expand Up @@ -373,7 +425,9 @@ def createApp(win):
V = TK.StringVar(win); V.set('Lonely edges: %d'%NL); VARS.append(V)
# -5- List of face to trim (compound 1)
V = TK.StringVar(win); V.set(''); VARS.append(V)

# -6- Number of components
V = TK.StringVar(win); V.set('Components: 0'); VARS.append(V)

# CAD file name
B = TTK.Entry(Frame, textvariable=VARS[0], background='White', width=15)
B.grid(row=0, column=0, sticky=TK.EW)
Expand All @@ -394,52 +448,63 @@ def createApp(win):

# Lonely edges
B = TTK.Label(Frame, textvariable=VARS[4])
B.grid(row=2, column=0, columnspan=2, sticky=TK.EW)
B.grid(row=2, column=0, columnspan=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Number of lonely edges.')

# Number of components
B = TTK.Label(Frame, textvariable=VARS[6])
B.grid(row=2, column=1, columnspan=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Number of connex watertight components.')

# Check watertight
B = TTK.Button(Frame, text="Check watertight", command=checkWatertight)
B.grid(row=3, column=0, columnspan=2, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Check if CAD is watertight.')
WIDGETS['checkWatertight'] = B

# Sewing
B = TTK.Button(Frame, text="Sew", command=sewCAD)
B.grid(row=3, column=0, sticky=TK.EW)
B.grid(row=4, column=0, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Sew CAD to fix multiple edges.')
WIDGETS['sewingButton'] = B

B = TTK.Entry(Frame, textvariable=VARS[2], background='White', width=10)
B.grid(row=3, column=1, sticky=TK.EW)
B.grid(row=4, column=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Sewing tolerance.')
B.bind('<Return>', sewCAD)

# Fillet
B = TTK.Button(Frame, text="Fillet", command=filletCAD)
B.grid(row=4, column=0, sticky=TK.EW)
B.grid(row=5, column=0, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Make a fillet from edges selection.')
WIDGETS['filletButton'] = B

B = TTK.Entry(Frame, textvariable=VARS[3], background='White', width=10)
B.grid(row=4, column=1, sticky=TK.EW)
B.grid(row=5, column=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Fillet radius.')
B.bind('<Return>', filletCAD)

# Remove faces
B = TTK.Button(Frame, text="Remove faces", command=removeFaces)
B.grid(row=5, column=0, columnspan=1, sticky=TK.EW)
B.grid(row=6, column=0, columnspan=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Remove selected faces from CAD.')
WIDGETS['removeFacesButton'] = B

# Fill hole
B = TTK.Button(Frame, text="Fill hole", command=fillHole)
B.grid(row=5, column=1, columnspan=1, sticky=TK.EW)
B.grid(row=6, column=1, columnspan=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Fill hole from CAD edges.')
WIDGETS['fillHoleButton'] = B

# Trim
B = TTK.Button(Frame, text="Trim faces", command=trimFaces)
B.grid(row=6, column=0, columnspan=1, sticky=TK.EW)
B.grid(row=7, column=0, columnspan=1, sticky=TK.EW)
BB = CTK.infoBulle(parent=B, text='Trim faces.')
WIDGETS['trimFacesButton'] = B
B = TTK.Button(Frame, command=setTrimFace1,
image=iconics.PHOTO[8], padx=0, pady=0)
BB = CTK.infoBulle(parent=B, text='Set the faces for first compound.')
B.grid(row=6, column=1, columnspan=1, sticky=TK.EW)
B.grid(row=7, column=1, columnspan=1, sticky=TK.EW)

#==============================================================================
# Called to display widgets
Expand Down
7 changes: 6 additions & 1 deletion Cassiopee/CPlot/apps/tkCADMesh.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# - generate surface mesh of CAD -
# - generate surface mesh of a CAD -
try: import tkinter as TK
except: import Tkinter as TK
import CPlot.Ttk as TTK
Expand All @@ -10,6 +10,8 @@
# local widgets list
WIDGETS = {}; VARS = []

#==============================================================================
# mesh cad edges
#==============================================================================
def meshCADEdges(event=None):
if CTK.CADHOOK is None: return
Expand Down Expand Up @@ -41,6 +43,8 @@ def meshCADEdges(event=None):
CTK.display(CTK.t)
CTK.TXT.insert('START', 'All CAD edges remeshed.\n')

#==============================================================================
# mesh cad faces
#==============================================================================
def meshCADFaces(event=None):
if CTK.CADHOOK is None: return
Expand All @@ -59,6 +63,7 @@ def meshCADFaces(event=None):
if mtype == 'TRI':
OCC._meshAllFacesTri(CTK.CADHOOK, CTK.t, hmax=hmax, hausd=hausd)
elif mtype == 'STRUCT':
OCC._remeshAllEdgesOdd(CTK.CADHOOK, CTK.t)
OCC._meshAllFacesStruct(CTK.CADHOOK, CTK.t)

CTK.setCursor(0, WIDGETS['frame'])
Expand Down
2 changes: 1 addition & 1 deletion Cassiopee/CPlot/test/deletePT.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
t = C.newPyTree(['Base', a, b, c])

CPlot.display(t); time.sleep(1)
CPlot.delete(['Base/cartTetra']); CPlot.render(); time.sleep(1)
CPlot.delete(['Base\cartTetra']); CPlot.render(); time.sleep(1)
2 changes: 1 addition & 1 deletion Cassiopee/Converter/Converter/kpython
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ else
fi
[ $? != 0 ] && exit 1;
;;
'visung_r8' | 'visung_r8_*' ) # visung
'visung_r8' | 'visung' ) # visung
# openMpi
mpirun $ARGS -x OMP_NUM_THREADS=$NTHREADS -np $NPROCS $PYTHONEXE $SCRIPT
[ $? != 0 ] && exit 1;
Expand Down
21 changes: 10 additions & 11 deletions Cassiopee/KCore/KCore/Nuga/include/Metric.h
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,9 @@ namespace DELAUNAY{
/// evaluation de la longeur d'un edge dans l'espace physique (avec la metrique)
template <typename T> inline
E_Float
VarMetric<T>::lengthEval (size_type Ni, const T& mi, size_type Nj, const T& mj)
VarMetric<T>::lengthEval(size_type Ni, const T& mi, size_type Nj, const T& mj)
{
E_Float v[2];
E_Float v[2]; // allocation sur la stack a supprimer
E_Float vi[2];
E_Float vj[2];
E_Float r1;
Expand Down Expand Up @@ -783,18 +783,18 @@ namespace DELAUNAY{
///
template <> inline
E_Float
VarMetric<E_Float>::lengthEval (size_type Ni, const E_Float& mi, size_type Nj, const E_Float& mj)
VarMetric<E_Float>::lengthEval(size_type Ni, const E_Float& mi, size_type Nj, const E_Float& mj)
{
// Warning : input mi and mj are in fact hi and hj.
return 0.5 * ((1./mi) + (1./mj)) * ::sqrt(NUGA::sqrDistance(_pos->col(Ni), _pos->col(Nj), _pos->rows()));
}

template <> inline
E_Float
VarMetric<DELAUNAY::Aniso3D>::lengthEval (size_type Ni, const DELAUNAY::Aniso3D& mi,
VarMetric<DELAUNAY::Aniso3D>::lengthEval(size_type Ni, const DELAUNAY::Aniso3D& mi,
size_type Nj, const DELAUNAY::Aniso3D& mj)
{
E_Float v[3];
E_Float v[3]; // allocation sur la stack a supprimer
E_Float vi[3];
E_Float vj[3];
E_Float r1;
Expand Down Expand Up @@ -831,25 +831,25 @@ namespace DELAUNAY{
return res;
}

///
/// Return curvature radius
template<> inline
E_Float
VarMetric<E_Float>::getRadius(size_type Ni)
{
return _field[Ni];
}

///
/// Return curvature radius
template<> inline
E_Float
VarMetric<Aniso2D>::getRadius(size_type Ni)
{
E_Float lmax,lmin;
E_Float lmax, lmin;
_field[Ni].eigen_values(lmax, lmin);
return 1./::sqrt(lmin);
}

///
/// Return curvature radius
template<> inline
E_Float
VarMetric<Aniso3D>::getRadius(size_type Ni)
Expand All @@ -868,13 +868,12 @@ namespace DELAUNAY{
// where X is the intersection point of the ellipse with the line (Ni, dir)

// (h2, dir) => X => hnew, the one between h1new and h2new that maximize hnew/hold

E_Float hdir = ::sqrt(hdir2);
E_Float NiX[] = {normed_dir[0]*hdir, normed_dir[1]*hdir};

// Diagonalization
E_Float lambda0, lambda1, v0[2], v1[2];
K_LINEAR::DelaunayMath::eigen_vectors (_field[Ni][0], _field[Ni][2], _field[Ni][1], lambda0, lambda1, v0, v1);
K_LINEAR::DelaunayMath::eigen_vectors(_field[Ni][0], _field[Ni][2], _field[Ni][1], lambda0, lambda1, v0, v1);
K_FLD::FloatArray D(2,2, 0.);
D(0,0) = lambda0;
D(1,1) = lambda1;
Expand Down
7 changes: 7 additions & 0 deletions Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,13 @@ E_Int __getParamHmaxHausd(const TopoDS_Edge& E, E_Float hmax, E_Float hausd, E_I
return ret;
}

// ============================================================================
// Return the nbPoints and ue for meshing E with hmin/hmax/hausd
// ============================================================================
//E_Int __getParamHminHmaxHausd(const TopoDS_Edge& E, E_Float hmin, E_Float hmax, E_Float hausd, E_Int& nbPoints, E_Float*& ue)
//{
//}

// ============================================================================
// Return ue for meshing with given param in [0,1]
// ============================================================================
Expand Down
1 change: 0 additions & 1 deletion Cassiopee/OCC/OCC/PyTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,6 @@ def _remeshAllEdgesOdd(hook, t):
if npts//2-0.5*npts == 0:
factor = (npts*1.)/(npts-1.)
G._refine(edge, factor, 1)
print('rem=',npts, C.getNPts(edge), factor)
D._getCurvilinearAbscissa(edge)
edgeno = getNo(edge)
aedge = C.getFields([Internal.__GridCoordinates__, Internal.__FlowSolutionNodes__], edge, api=2)[0]
Expand Down

0 comments on commit dec548d

Please sign in to comment.