Issue with projection of edges with seam to create section of a face

Hello,
i have a torus face and 4 edges that each edge share their vertexes positions with vertexes of other two edges (ie., they are a closed loop, prove if i create a compound of the 4 edges and use partition with it itself (without fuzzy!), it returns 4 edges and 4 vertexes).
the edges were projected over the torus face, with the execption of one of them that as it is over the seam of the torus face, when doing the projection it is horribly failing (wrong projection over torus seam · Issue #12 · SalomePlatform/geom · GitHub). for the ‘problematic’ edge, what i did instead of projecting it, is using the seam edge of the torus face, i use partition (without fuzzy!) over it using the two extreme vertexes of the edge that i wanted to ‘project’ and then keeping only the edge that would be the ‘projection’.
what I am looking to do is cut and keep only the inside of the torus face between these edges. so there is two approaches for this:

  1. use create face with build face 2nd option (face creation from surface bounded by wire)

pros:

  • it gives ‘more or less’ the correct result, ie, what looks like the desired face

cons:

  • it is using an approximation! (as when creating a wire there is a tolerance parameter where the 2. workflow it does not need at any point this tolerances) this is really problematic for me as I am looking to use the ‘exact’ edges (reason why i am not using fuzzy) as i need to merge it with other faces created with some of the same edges
  • the ‘problematic’ edge (the one over the seam) results into several ‘small edges’ with a lot of vertex, obviously also problematic for previous reasons
  1. create a partition (without fuzzy!) of the original face and the projected edges (which one is the result of the cut of the seam instead of a projected object from the projection tool, as mentioned before) and then explode the partition result into faces and with some heuristics get the correct face

pros:

  • there are no tolerances whats so ever
  • it is using the edges and not the wire

cons:

  • it is not working. this workflow has been working for any kind of cannonical type of faces without any issues, spheres, cylinders (ie., ‘SPHERE2D’,‘CYLINDER2D’) but with the ‘TORUS2D’ this is completly failing horrible.

so:
face +edges (with showing vertexes)


the resulting ‘parition’ of the face with the edges:

what I thought to do would be when I create the face (using method 2.) to check if it was ‘correctly’ created using:
if not geompy.CheckShape(newFace, theIsCheckGeom = 1, theReturnStatus = 0):
and if thats is true then fall back and use the 1. method, nevertheless this proved to be problematic as even the resulting strange face of the second image the output of CheckShape is still True. I thought this solution:

wire = geompy.MakeWire(edges, 1e-07)
[Xmin,Xmax, Ymin,Ymax, Zmin,Zmax] = geompy.BoundingBox(wire, True)
[Xmin_,Xmax_, Ymin_,Ymax_, Zmin_,Zmax_] = geompy.BoundingBox(newFace, True)
deltaX=Xmax-Xmin
deltaY=Ymax-Ymin
deltaZ=Zmax-Zmin
deltaX_=Xmax_-Xmin_
deltaY_=Ymax_-Ymin_
deltaZ_=Zmax_-Zmin_
flagToRecreate=not geompy.CheckShape(newFace, theIsCheckGeom = 1, theReturnStatus = 0)
if not(deltaX==0 and deltaX_==0):
    errorX=abs(deltaX_-deltaX)/deltaX_*100
    if errorX>FractionError:
        flagToRecreate=True
if not(deltaY==0 and deltaY_==0):
    errorY=abs(deltaY_-deltaY)/deltaY_*100
    if errorY>FractionError:
        flagToRecreate=True
if not(deltaZ==0 and deltaZ_==0):
    errorZ=abs(deltaZ_-deltaZ)/deltaZ_*100
    if errorZ>FractionError:
        flagToRecreate=True
if  flagToRecreate :
    print('error or problem with the newFace created! creating it using entity and the wire of edges')
    newFace = geompy.MakeFaceFromSurface(entity, wire)

this is a middle ground solution, but it is giving me a big issue, when doing in the TUI, SOMETIMES the geompy.MakeFaceFromSurface(entity, wire) keeps the ‘wrong’ side, but this does not happen in the GUI:
result from the TUI
image
this was obtained by doing:

wire = geompy.MakeWire(edges, 1e-07)
newFace = geompy.MakeFaceFromSurface(entity, wire)
geompy.addToStudy(entity,'face '+str(nFace))
geompy.addToStudy(wire,'wire of face '+str(nFace))
geompy.addToStudy(newFace,'newFace '+str(nFace))

nFace=19
neverthless if i take the object (‘face 19’ and ‘wire of face 19’) in the object browser and do:
→ build face → face creation from surface bounded by wire (2nd option) → select ‘face 19’ and ‘wire of face 19’ on the object browser and apply it I get the correct result:
image
so there is a clear discrepancy between the result from the TUI and the GUI

  • how can I obtain the same result in the TUI?
  • I am missing an option? flag?
  • is there an extra step that is not done when doing newFace = geompy.MakeFaceFromSurface(entity, wire) ?

in the information dialog of the object browser i have exactly the same thing for the result from the TUI and from the GUI:
image

I need to solve this as fast as I can as it is blocking my development, any inputs would be apreciated.

The ONLY difference I see between the object created in the TUI and GUI is that in the object browser the one from the GUI has one less dependency?
image

also if I check projected 19 face with checkFace in the interface i dont get any errors at all from it.
if use shape processing with defaults flags, i get ‘small edge removed’ count 2, but i dont see any difference in the edges but some vertexes disapear (following image the processed geometry with displaymode show vertexes, notice that in the ‘cut’ part there are missing vertexes!)
image

also another thing I just notice that maybe could give a clue to the developers is that, the original face (‘face 19’) is a SECTION of a torus and not the complete one.
but when I see the ‘section’ that i am getting in the TUI. the part that was missing in ‘face 19’ is there again:
here we see the ‘face 19’ with transparency on and ‘wire of face 19’
image
here is ‘newFace 19’ (the one done in the TUI) notice the extra part in the upper side of the torus and also that the seam of the cross section is not positioned correctly (it should be in same place as the original geometry)
image

please some help in understanding this, would be appreciated

@cbourcier any idea? sugestions? i am completly blocked just for this annoying faces that give the wrong result.

Hi Franco,

do you absolutely need to keep this workflow?

It works with something like this:

  • create a face from the straight edge + projected edge
  • make a revolution of it with 90 degrees around OZ
  • cut the torus face

It works even without creating the face, but creating a revolution from the projected edge. Look at the different cuts in Study_cut.hdf (281,5 Ko).

Maybe there are issues with your original points and edges?

Christophe

hello christophe,
sorry for bothering,
first of all, the partition_3 in your fille looks ‘wrong’ for me in salome.
image

for you question,
well, currently, I have developed a complete script that, takes as input a blocked and not blocked geometry (ie cad for structured meshing). it mesh the blocked geometry and then it smooth it out in volumetric (so 3D) this work great, i also identified the nodes with each feature of the blocked geometry. then the idea (which is the last step of this workflow) is from the nodes positions recreate the blocked geometry but smoothed (so a cad for structured meshing BUT smoothed), so then one can use this cad model and get smoothed meshes.

this works until this problematic surfaces (torus), i can rotate the torus using (at least what i found possible a reconstructed axis from geompy.KindOfShape data, but this gives issues as i imagine that are the numerical errors (slight different decimal points) that make that the edges are not directly overlapsed with the original face (that was rotated) so i would like to ‘not recreate the original face and use the original one’ as i have spended almost a week trying that way and it gave a lot of problems. the vertexes (points) are also projected over the surface before creating the edges, the workflow is:
create vertexes (corners of blocks) if they are associated with an edge or a face they are projected
create edges that uses the previously created vertexes and also project them to a face if they are associated and if they are ‘over’ an original edge simply get the new edge by partition (as i mentionned previously ‘with the problematic edge’)
and then create the faces if they are external ones, using the faces of the not blocked geometry and ‘cuts it using the edges created’ if they are internal it fills them using the edges.
right now, i could do what you mention, but this is because it is the before smoothed blocking, so it is a simple rotation the face, but after smoothed out the face could take another form and therefore this would not be possible.
i am at more than 2.5k lines, and this is the only blocking point. even if the blocked geometry does not have torus this would not be an issue. I would like to solve it specially when i clearly see that in the GUI it works as expected.

Hi Franco,

please share a minimal example of what works in GUI and does not work in python.

Christophe

Hello christophe,
here you have the hdf of the script runned, for info:
the python variables of the objects:

  • entityClean== face to project on (ie, to cut with the edges)
  • edgesToCut== list of edges that is cutting the face
  • wire== wire created from the list of edges (edgesToCut)
  • newFace== the newFace created using the wire and entityClean

I can not share the complete script, but here is the section (function to create newFace) where this is happening:


def createNewFace(edges,internalVertexes=[],entity=None,nFace='',debug=False):
    global newFace,entityClean,edgesToCut,wire
    edgesToCut=edges
    FractionError=10
    newFace=[]
    if entity:
        """
        this can fail, giving the 'remaining' of the surface instead of the desired surface!
        wire = geompy.MakeWire(edges, 1e-07)
        newFace = geompy.MakeFaceFromSurface(entity, wire)
        if debug:
            geompy.addToStudy(wire,'wireToCreateFace '+str(nFace))
        """
        # wireOfEdges=geompy.MakeWire(edges, 1e-07)
        wireOfEdges=geompy.MakeCompound(edges)
        centerOfEdges=geompy.MakeCDG(wireOfEdges)

        # entityClean=recreateFaceForProjection(centerOfEdges,entity)
        entityClean=entity
        roundingErrorDecimals=5
        facePartition=geompy.MakePartition([entityClean], edges, [], [], geompy.ShapeType["FACE"], 0, [], 0)
        facesCutSections=[face for face in geompy.SubShapeAll(facePartition, geompy.ShapeType["FACE"])]
        possibleFacesSections=[cleanFace(face) for face in facesCutSections if all([round(geompy.MinDistance(vertex, wireOfEdges),roundingErrorDecimals)==0.0 for vertex in geompy.SubShapeAll(face, geompy.ShapeType["FACE"])])]
        if len(possibleFacesSections):
            nVertsOrig=geompy.NumberOfSubShapes(wireOfEdges, geompy.ShapeType["VERTEX"])
            possibleFacesSectionsRatings=[geompy.NumberOfSubShapes(p, geompy.ShapeType["VERTEX"])/nVertsOrig for p in possibleFacesSections]
            if 1 in possibleFacesSectionsRatings:
                if possibleFacesSectionsRatings.count(1)==1:
                    possibleFacesSections=[possibleFacesSections[possibleFacesSectionsRatings.index(1)]]
            if len(possibleFacesSections)>1:
                testerFace=geompy.MakeFaceWires(edges, isPlanarWanted=True)
                centerTesterFace=geompy.MakeVertexOnSurface(testerFace,0.5,0.5)
                centerTestFaceDist=[geompy.MinDistance(centerTesterFace,geompy.MakeVertexOnSurface(testFace,0.5,0.5)) for testFace in possibleFacesSections]
                newFace=possibleFacesSections[centerTestFaceDist.index(min(centerTestFaceDist))]
            else:
                newFace=possibleFacesSections[0]
        else:
            print('error no possibleFacesSections! returning face from filled edges (geompy.MakeFaceWires())')
            newFace=geompy.MakeFaceWires(edges, isPlanarWanted=False)
            geompy.addToStudy(newFace,'resulting problematic face to recreate')
        
        wire = geompy.MakeWire(edges, 1e-07)
        [Xmin,Xmax, Ymin,Ymax, Zmin,Zmax] = geompy.BoundingBox(wire, True)
        [Xmin_,Xmax_, Ymin_,Ymax_, Zmin_,Zmax_] = geompy.BoundingBox(newFace, True)
        deltaX=Xmax-Xmin
        deltaY=Ymax-Ymin
        deltaZ=Zmax-Zmin
        deltaX_=Xmax_-Xmin_
        deltaY_=Ymax_-Ymin_
        deltaZ_=Zmax_-Zmin_
        flagToRecreate=not geompy.CheckShape(newFace, theIsCheckGeom = 1, theReturnStatus = 0)
        if not(deltaX==0 and deltaX_==0):
            errorX=abs(deltaX_-deltaX)/deltaX_*100
            if errorX>FractionError:
                flagToRecreate=True
        if not(deltaY==0 and deltaY_==0):
            errorY=abs(deltaY_-deltaY)/deltaY_*100
            if errorY>FractionError:
                flagToRecreate=True
        if not(deltaZ==0 and deltaZ_==0):
            errorZ=abs(deltaZ_-deltaZ)/deltaZ_*100
            if errorZ>FractionError:
                flagToRecreate=True

        if  flagToRecreate :
            geompy.addToStudy(entity,'entity '+str(nFace))
            [geompy.addToStudy(edge,'edge '+str(nEdge)+' of face '+str(nFace)) for nEdge,edge in enumerate(edges)]
            geompy.addToStudy(wire,'wire of face '+str(nFace))
            print('error or problem with the newFace '+str(nFace)+' created! creating it using entity and the wire of edges')
            newFace = geompy.MakeFaceFromSurface(entity, wire)
            geompy.addToStudy(newFace,'new face '+str(nFace))
    return newFace

as you can see I added to the object browser the different entities used and created while running the function. the hdf file I am sending to you in private as it is too large and has some data that would like to not share in the forum, the study is when the scrip is run . As it can be seen in the screen capture in the object browser of the hdf file the published objects from the function are shown:
image
as i mentionned before, this function for other kind of faces it is working correctly, spheres, cylindres, etc.

There is indeed a flag Inside for BRepBuilderAPI_MakeFace which is called by MakeFaceFromSurface but its default value is not changed between python and GUI, since this parameter is not specified.

In fact, if you call several times MakeFaceFromSurface in python, you can get the desired result face. First time is not what you want (the complementary face), next times it is correct (the small face). I think it is because your wire has different orientations for its edges.

Their orientations can be changed in python script so that they are coherent for consecutive edges.

But it gives the same result as before.

Changing orientation of the whole wire gives a good result for the 20 times.

Have a look at the script in test_make_face_reorient_edges.zip (94,8 Ko)

So, for your generic script, either call MakeFaceFromSurface several times until you get the desired result, either change the orientation of the edges to make them coherent.

Christophe

Hello again christophe,
thanks for your help,
I suspected that it was related to the orientation, but i had experienced making several faces over this torous with some having correct directions, some of them having oposit direction etc and sometimes it worked sometimes no, but good thing is now that you gave me that clue I can trace back the correct orientation.
the ‘random’ behavior of salome (in python) as it is not the same as in GUI should be corrected, no? does it worth making a git issue?
regards,
franco

I have submitted on issue on our bugtracker to make this function gives reproducible results.

Christophe

1 Like

re hello christophe,
I am still facing some issues, I am re ordering the edges of the wire so they are one after the other:
image
nevertheless i am getting this output;
image
this has also happened here: Issue with projection of edges with seam to create section of a face - #5 by franco.ota i dont know exactly what the problem is. (still in the gui with the published elements that where used to get the second image it gives the correct result)
If I repeat as you mentioned at on point I get the correct result nevertheless this behavior is even more problematic as it is not even a section of the face…

Hi Franco,

Please share a detailed example on how to reproduce what fails. From the script I provided, partition works with the wire reoriented reversely in GUI and python:

Partition_1= geompy.MakePartition([entity_19_brep_1], [Wire_reoriented_reversed], theName="Partition_1")

Christophe

Hello christophe,
even in this example Issue with projection of edges with seam to create section of a face - #4 by cbourcier for partition_3 I see this. I reoriented all edges of the wire and sometimes it works sometimes it does not (in contrast with you that you mentionned that you where getting 20/20) sometimes i get as last image of my previous comment.

on other side, I am also having a different behavior between glue faces in TUI than in GUI. I have recreated all my blocks, make a compound of them and use glue faces with tolerance of 1e-7.
in the case of the tui, eventhought it does not give any error during the steps, the mesh of it, gives errors and the problematic solid looks like this (which should be ‘void’ and not a solid on the compoind (as it is not part of the solids that made the compound, only it shares faces but should be a closed empty section on the compound):
image
image

but if I publish all the blocks, create a partition of them and then use glue faces in the gui, the meshing process gives expected result (and correct).
i can send you the file in private if necessary.
extra info: if in the TUI i do a geompy.LimitTolerance(resultOfGlueFaces,1e-7) i get a correct geometry (this is not necessary with same objects on the GUI) how can this happen? i never set a tolerance during contruction different to 1e-7. furthermore in the gui it works, in the tui no.