From dec548d59d4366653b191a220e45b4dfc860730a Mon Sep 17 00:00:00 2001 From: benoit128 Date: Wed, 13 Nov 2024 12:33:57 +0100 Subject: [PATCH] tkCADFix: add check watertight --- Cassiopee/CPlot/CPlot/PyTree.py | 8 +- Cassiopee/CPlot/apps/tkBlock.py | 2 +- Cassiopee/CPlot/apps/tkCADFix.py | 85 ++++++++++++++++++--- Cassiopee/CPlot/apps/tkCADMesh.py | 7 +- Cassiopee/CPlot/test/deletePT.py | 2 +- Cassiopee/Converter/Converter/kpython | 2 +- Cassiopee/KCore/KCore/Nuga/include/Metric.h | 21 +++-- Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp | 7 ++ Cassiopee/OCC/OCC/PyTree.py | 1 - 9 files changed, 105 insertions(+), 30 deletions(-) diff --git a/Cassiopee/CPlot/CPlot/PyTree.py b/Cassiopee/CPlot/CPlot/PyTree.py index b1a15ba1f..dd84fe5cc 100644 --- a/Cassiopee/CPlot/CPlot/PyTree.py +++ b/Cassiopee/CPlot/CPlot/PyTree.py @@ -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. diff --git a/Cassiopee/CPlot/apps/tkBlock.py b/Cassiopee/CPlot/apps/tkBlock.py index d3ddc2068..cb94e2913 100644 --- a/Cassiopee/CPlot/apps/tkBlock.py +++ b/Cassiopee/CPlot/apps/tkBlock.py @@ -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) diff --git a/Cassiopee/CPlot/apps/tkCADFix.py b/Cassiopee/CPlot/apps/tkCADFix.py index 6476332b6..ab8dc8926 100644 --- a/Cassiopee/CPlot/apps/tkCADFix.py +++ b/Cassiopee/CPlot/apps/tkCADFix.py @@ -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 #============================================================================== @@ -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) @@ -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('', 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('', 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 diff --git a/Cassiopee/CPlot/apps/tkCADMesh.py b/Cassiopee/CPlot/apps/tkCADMesh.py index 05f70d6ff..63b19d5ae 100644 --- a/Cassiopee/CPlot/apps/tkCADMesh.py +++ b/Cassiopee/CPlot/apps/tkCADMesh.py @@ -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 @@ -10,6 +10,8 @@ # local widgets list WIDGETS = {}; VARS = [] +#============================================================================== +# mesh cad edges #============================================================================== def meshCADEdges(event=None): if CTK.CADHOOK is None: return @@ -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 @@ -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']) diff --git a/Cassiopee/CPlot/test/deletePT.py b/Cassiopee/CPlot/test/deletePT.py index 294278b76..bbe596eb9 100644 --- a/Cassiopee/CPlot/test/deletePT.py +++ b/Cassiopee/CPlot/test/deletePT.py @@ -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) diff --git a/Cassiopee/Converter/Converter/kpython b/Cassiopee/Converter/Converter/kpython index b8d6185f7..c0f0df003 100755 --- a/Cassiopee/Converter/Converter/kpython +++ b/Cassiopee/Converter/Converter/kpython @@ -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; diff --git a/Cassiopee/KCore/KCore/Nuga/include/Metric.h b/Cassiopee/KCore/KCore/Nuga/include/Metric.h index 9332b9e24..6afcaabc7 100755 --- a/Cassiopee/KCore/KCore/Nuga/include/Metric.h +++ b/Cassiopee/KCore/KCore/Nuga/include/Metric.h @@ -722,9 +722,9 @@ namespace DELAUNAY{ /// evaluation de la longeur d'un edge dans l'espace physique (avec la metrique) template inline E_Float - VarMetric::lengthEval (size_type Ni, const T& mi, size_type Nj, const T& mj) + VarMetric::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; @@ -783,7 +783,7 @@ namespace DELAUNAY{ /// template <> inline E_Float - VarMetric::lengthEval (size_type Ni, const E_Float& mi, size_type Nj, const E_Float& mj) + VarMetric::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())); @@ -791,10 +791,10 @@ namespace DELAUNAY{ template <> inline E_Float - VarMetric::lengthEval (size_type Ni, const DELAUNAY::Aniso3D& mi, + VarMetric::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; @@ -831,7 +831,7 @@ namespace DELAUNAY{ return res; } - /// + /// Return curvature radius template<> inline E_Float VarMetric::getRadius(size_type Ni) @@ -839,17 +839,17 @@ namespace DELAUNAY{ return _field[Ni]; } - /// + /// Return curvature radius template<> inline E_Float VarMetric::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::getRadius(size_type Ni) @@ -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; diff --git a/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp b/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp index 18ac76e3d..89f6e5162 100644 --- a/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp +++ b/Cassiopee/OCC/OCC/Atomic/meshEdge2.cpp @@ -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] // ============================================================================ diff --git a/Cassiopee/OCC/OCC/PyTree.py b/Cassiopee/OCC/OCC/PyTree.py index aecdcd24a..bf7776566 100644 --- a/Cassiopee/OCC/OCC/PyTree.py +++ b/Cassiopee/OCC/OCC/PyTree.py @@ -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]