From d9a5ccba7c4db682b434f9c5662ba630ce23f940 Mon Sep 17 00:00:00 2001 From: benoit128 Date: Mon, 4 Nov 2024 17:09:04 +0100 Subject: [PATCH] OCC: add trimFaces --- Cassiopee/CPlot/apps/tkCADFix.py | 105 +++++++++++++++++++++++- Cassiopee/OCC/OCC/Atomic/sewing.cpp | 2 +- Cassiopee/OCC/OCC/Atomic/trim.cpp | 122 +++++++++++++++++++++++----- Cassiopee/OCC/OCC/PyTree.py | 5 ++ 4 files changed, 208 insertions(+), 26 deletions(-) diff --git a/Cassiopee/CPlot/apps/tkCADFix.py b/Cassiopee/CPlot/apps/tkCADFix.py index 9c03af784..6476332b6 100644 --- a/Cassiopee/CPlot/apps/tkCADFix.py +++ b/Cassiopee/CPlot/apps/tkCADFix.py @@ -6,6 +6,7 @@ import CPlot.PyTree as CPlot import CPlot.Tk as CTK import Converter.Internal as Internal +import CPlot.iconics as iconics # local widgets list WIDGETS = {}; VARS = [] @@ -143,7 +144,7 @@ def removeFaces(event=None): if CTK.CADHOOK is None: return hook = CTK.CADHOOK [hmax, hausd] = OCC.getCADcontainer(CTK.t) - # Get selected edges + # Get selected faces nzs = CPlot.getSelectedZones() faces = [] for nz in nzs: @@ -244,6 +245,91 @@ def fillHole(event=None): CTK.display(CTK.t) CTK.TXT.insert('START', 'Fill hole in CAD.\n') +#============================================================================== +def setTrimFace1(): + if CTK.t == []: return + if CTK.__MAINTREE__ <= 0: + CTK.TXT.insert('START', 'Fail on a temporary tree.\n') + CTK.TXT.insert('START', 'Error: ', 'Error'); return + nzs = CPlot.getSelectedZones() + if nzs == []: + CTK.TXT.insert('START', 'Selection is empty.\n') + CTK.TXT.insert('START', 'Error: ', 'Error'); return + selected = '' + for nz in nzs: + nob = CTK.Nb[nz]+1 + noz = CTK.Nz[nz] + b = CTK.t[2][nob] + if b[0] != 'FACES': continue + z = CTK.t[2][nob][2][noz] + CAD = Internal.getNodeFromName1(z, 'CAD') + if CAD is not None: + no = Internal.getNodeFromName1(CAD, 'no') + no = Internal.getValue(no) + selected += str(no)+' ' + print(selected, flush=True) + VARS[5].set(selected) + +#============================================================================== +def trimFaces(event=None): + import OCC.PyTree as OCC + if CTK.CADHOOK is None: return + hook = CTK.CADHOOK + [hmax, hausd] = OCC.getCADcontainer(CTK.t) + # Get selected faces + nzs = CPlot.getSelectedZones() + faces1 = [] + for nz in nzs: + nob = CTK.Nb[nz]+1 + noz = CTK.Nz[nz] + b = CTK.t[2][nob] + if b[0] != 'FACES': continue + z = CTK.t[2][nob][2][noz] + CAD = Internal.getNodeFromName1(z, 'CAD') + if CAD is not None: + no = Internal.getNodeFromName1(CAD, 'no') + no = Internal.getValue(no) + faces1.append(no) + if faces1 == []: + CTK.TXT.insert('START', 'No valid faces in selection.\n') + return + + faces2 = [] + st = VARS[5].get() + st = st.split(' ') + for s in st: + try: + val = int(s) + faces2.append(val) + except: pass + if faces2 == []: + CTK.TXT.insert('START', 'No valid faces in selection.\n') + return + + CTK.setCursor(2, WIDGETS['frame']) + CTK.setCursor(2, WIDGETS['trimFacesButton']) + + OCC.occ.trimFaces(hook, faces1, faces2) + + # remesh CAD and redisplay + edges = Internal.getNodeFromName1(CTK.t, 'EDGES') + edges[2] = [] + faces = Internal.getNodeFromName1(CTK.t, 'FACES') + faces[2] = [] + OCC._meshAllEdges(hook, CTK.t, hmax=hmax, hausd=hausd) # loose manual remeshing... + OCC._meshAllFacesTri(hook, CTK.t, hmax=hmax, hausd=hausd) + + CTK.setCursor(0, WIDGETS['frame']) + CTK.setCursor(0, WIDGETS['trimFacesButton']) + NL = OCC.getNbLonelyEdges(CTK.t) + VARS[4].set('Lonely edges: %d'%NL) + + (CTK.Nb, CTK.Nz) = CPlot.updateCPlotNumbering(CTK.t) + CTK.TKTREE.updateApp() + CTK.display(CTK.t) + + CTK.TXT.insert('START', 'Faces trimmed in CAD.\n') + #============================================================================== # Create app widgets #============================================================================== @@ -285,13 +371,15 @@ def createApp(win): V = TK.StringVar(win); V.set('0.1'); VARS.append(V) # -4- Bilan des edges lonely 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) # CAD file name B = TTK.Entry(Frame, textvariable=VARS[0], background='White', width=15) B.grid(row=0, column=0, sticky=TK.EW) BB = CTK.infoBulle(parent=B, text='CAD file name.') - # CAD fille format + # CAD file format B = TTK.OptionMenu(Frame, VARS[1], 'fmt_step', 'fmt_iges') B.grid(row=0, column=1, sticky=TK.EW) @@ -333,16 +421,25 @@ def createApp(win): # Remove faces B = TTK.Button(Frame, text="Remove faces", command=removeFaces) - B.grid(row=5, column=0, columnspan=2, sticky=TK.EW) + B.grid(row=5, 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=6, column=0, columnspan=2, sticky=TK.EW) + B.grid(row=5, 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) + 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) #============================================================================== # Called to display widgets diff --git a/Cassiopee/OCC/OCC/Atomic/sewing.cpp b/Cassiopee/OCC/OCC/Atomic/sewing.cpp index db8516499..d15e81ee8 100644 --- a/Cassiopee/OCC/OCC/Atomic/sewing.cpp +++ b/Cassiopee/OCC/OCC/Atomic/sewing.cpp @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Cassiopee. If not, see . */ -// CAD split for parallel +// CAD sewing #include "occ.h" #include "TopoDS.hxx" diff --git a/Cassiopee/OCC/OCC/Atomic/trim.cpp b/Cassiopee/OCC/OCC/Atomic/trim.cpp index 5558ce84c..74582c259 100644 --- a/Cassiopee/OCC/OCC/Atomic/trim.cpp +++ b/Cassiopee/OCC/OCC/Atomic/trim.cpp @@ -24,19 +24,21 @@ #include "BRepBuilderAPI_MakeWire.hxx" #include "ShapeUpgrade_UnifySameDomain.hxx" #include "BRepFeat_SplitShape.hxx" +#include "BRep_Builder.hxx" #include "TopExp.hxx" #include "TopExp_Explorer.hxx" #include "TopTools_IndexedMapOfShape.hxx" #include "ShapeBuild_ReShape.hxx" #include "TopoDS.hxx" +#include "BRepAlgoAPI_Cut.hxx" //===================================================================== // Fix the full shape //===================================================================== PyObject* K_OCC::trimFaces(PyObject* self, PyObject* args) { - PyObject* hook; PyObject* listOfFaceNo; PyObject* listOfEdgeNo; - if (!PYPARSETUPLE_(args, OOO_ , &hook, &listOfFaceNo, &listOfEdgeNo)) return NULL; + PyObject* hook; PyObject* listOfFaceNo1; PyObject* listOfFaceNo2; + if (!PYPARSETUPLE_(args, OOO_ , &hook, &listOfFaceNo1, &listOfFaceNo2)) return NULL; void** packet = NULL; #if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 7) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 1) @@ -46,33 +48,76 @@ PyObject* K_OCC::trimFaces(PyObject* self, PyObject* args) #endif TopTools_IndexedMapOfShape& surfaces = *(TopTools_IndexedMapOfShape*)packet[1]; - TopTools_IndexedMapOfShape& edges = *(TopTools_IndexedMapOfShape*)packet[2]; + //TopTools_IndexedMapOfShape& edges = *(TopTools_IndexedMapOfShape*)packet[2]; - TopoDS_Shape* shp = (TopoDS_Shape*)packet[0]; - TopoDS_Shape* newshp = new TopoDS_Shape(); - - // Build a wire - BRepBuilderAPI_MakeWire wireBuilder; - for (E_Int i = 0; i < PyList_Size(listOfEdgeNo); i++) + // Build remaining faces list + std::list pl; + E_Int nfaces = surfaces.Extent(); + for (E_Int i = 1; i <= nfaces; i++) pl.push_back(i); + + // Build compound from face1 + BRep_Builder builder1; + TopoDS_Compound compound1; + builder1.MakeCompound(compound1); + for (E_Int i = 0; i < PyList_Size(listOfFaceNo1); i++) { - PyObject* edgeNoO = PyList_GetItem(listOfEdgeNo, i); - E_Int edgeNo = PyLong_AsLong(edgeNoO); - const TopoDS_Edge& E = TopoDS::Edge(edges(edgeNo)); - wireBuilder.Add(E); + PyObject* faceNoO = PyList_GetItem(listOfFaceNo1, i); + E_Int faceNo = PyLong_AsLong(faceNoO); + auto it = std::find(pl.begin(), pl.end(), faceNo); + if (it != pl.end()) pl.erase(it); + const TopoDS_Face& F = TopoDS::Face(surfaces(faceNo)); + builder1.Add(compound1, F); } - TopoDS_Wire W = wireBuilder.Wire(); - BRepFeat_SplitShape splitter(*shp); - // Add faces to splitter - for (E_Int i = 0; i < PyList_Size(listOfFaceNo); i++) + // Build compound from face2 + BRep_Builder builder2; + TopoDS_Compound compound2; + builder1.MakeCompound(compound2); + for (E_Int i = 0; i < PyList_Size(listOfFaceNo2); i++) { - PyObject* faceNoO = PyList_GetItem(listOfFaceNo, i); + PyObject* faceNoO = PyList_GetItem(listOfFaceNo2, i); E_Int faceNo = PyLong_AsLong(faceNoO); + auto it = std::find(pl.begin(), pl.end(), faceNo); + if (it != pl.end()) pl.erase(it); const TopoDS_Face& F = TopoDS::Face(surfaces(faceNo)); - splitter.Add(W, F); + builder2.Add(compound2, F); + } + + // trim the compound + TopoDS_Shape trimmedCompound1 = BRepAlgoAPI_Cut(compound1, compound2); + TopoDS_Shape trimmedCompound2 = BRepAlgoAPI_Cut(compound2, compound1); + + // rebuild + BRep_Builder builder3; + TopoDS_Compound compound3; + builder3.MakeCompound(compound3); + + for (auto& i : pl) + { + TopoDS_Face F = TopoDS::Face(surfaces(i)); + builder3.Add(compound3, F); } - splitter.Build(); - *newshp = splitter.Shape(); + + TopExp_Explorer expl1(trimmedCompound1, TopAbs_FACE); + while (expl1.More()) + { + TopoDS_Shape shape = expl1.Current(); + TopoDS_Face face = TopoDS::Face(shape); + builder3.Add(compound3, face); + expl1.Next(); + } + TopExp_Explorer expl2(trimmedCompound2, TopAbs_FACE); + while (expl2.More()) + { + TopoDS_Shape shape = expl2.Current(); + TopoDS_Face face = TopoDS::Face(shape); + builder3.Add(compound3, face); + expl2.Next(); + } + + TopoDS_Shape* newshp = new TopoDS_Shape(compound3); + //TopoDS_Shape* newshp = new TopoDS_Shape(trimmedCompound2); + // Rebuild the hook packet[0] = newshp; @@ -95,3 +140,38 @@ PyObject* K_OCC::trimFaces(PyObject* self, PyObject* args) Py_INCREF(Py_None); return Py_None; } + +//=== +/* +#include +#include +#include +#include +#include +#include +#include +#include + +Handle(Geom_Surface) surface1 = ...; // Your first surface +Handle(Geom_Surface) surface2 = ...; // Your second surface +TopoDS_Face face1 = BRepBuilderAPI_MakeFace(surface1, Precision::Confusion()); +TopoDS_Face face2 = BRepBuilderAPI_MakeFace(surface2, Precision::Confusion()); + +BRepAlgoAPI_Section section(face1, face2); +section.ComputePCurveOn1(Standard_True); +section.Approximation(Standard_True); +section.Build(); + +if (!section.IsDone()) { + std::cerr << "Error: Intersection computation failed." << std::endl; + return; +} + +TopoDS_Shape intersection = section.Shape(); +TopoDS_Wire wire = BRepBuilderAPI_MakeWire(TopoDS::Edge(intersection)); +TopoDS_Face trimmedFace1 = BRepBuilderAPI_MakeFace(surface1, wire, Standard_True); +TopoDS_Face trimmedFace2 = BRepBuilderAPI_MakeFace(surface2, wire, Standard_True); + +// on compounds == +TopoDS_Shape trimmedCompound = BRepAlgoAPI_Cut(compound1, compound2); +*/ \ No newline at end of file diff --git a/Cassiopee/OCC/OCC/PyTree.py b/Cassiopee/OCC/OCC/PyTree.py index 620fc9a45..f87a46864 100644 --- a/Cassiopee/OCC/OCC/PyTree.py +++ b/Cassiopee/OCC/OCC/PyTree.py @@ -1235,6 +1235,11 @@ def _fillHole(hook, edges): OCC.occ.fillHole(hook, edges) return None +# trim two set of surfaces +def _trimFaces(hook, faces1, faces2): + OCC.occ.trimFaces(hook, faces1, faces2) + return None + # Return the number of edges in CAD hook def getNbEdges(hook): """Return the number of edges in CAD hook."""