Brep.Flip(), Brep.Face.Reverse(), Brep.Surface.Reverse Inconsistencies

You can get the normals of a Surface or a BrepFace by using the NormalAt method. I have experimented with this a bit and found out that it is not consistent. I am hoping someone can chime in on this topic and explain what the purpose of the inconsistencies are. There must be more to it than I’m not seeing.

Whenever I used the Brep.Flip(), Face.Reverse(0, True), or Surface.Reverse(0, True) methods, I expected the same results. I assumed, maybe incorrectly, that whenever you flip or reverse a face or surface that it would do the same for the other type of object.

I have created a surface / brep face and duplicated it 3 times. On each object using python script, I display a point representing the direction of the normal retrieved from Surface.NormalAt() by a blue point and a point representing the direction of the normal retrieved from Face.NormalAt() by a red point. I used the dir command to show that the normals on all 4 objects are pointing to the left.

On the first (bottom) object I have done just that. Both points ended up left of the object. This was expected as the normals from the dir command point left as well. I also interrogate Face.OrientationIsReversed which is False. I do this just to inspect what’s going on as I apply different methods.

On the second object I use the Brep.Flip() method to flip the object and therefore I thought the surface and the face would be flipped. Turns out the surface remained the same which was not expected. It did not flip. Furthermore, the face flipped so the red point moved to the other side. This was expected. What was unexpected is that Face.OrientationIsReversed was set to True. I would have expected either the point would have moved to the other side OR Face.OrientationIsReversed be set to true but NOT BOTH.

With the third object I used Face.Reverse(0, True) to reverse the object. I expected the surface normal point to be on the right. I also expected either the face normal point to be on the right or Face.OrientationIsReversed to be set to True, but not both. This is exactly what happened. So the surface normal point ended up on the other side. The face normal point did not move yet Face.OrientationIsReversed was set to True as expected.

On the last object I used Surface.Reverse(0, True) to reverse the object. I expected the surface normal point to be on the right. I also expected either the face normal point to be on the right or Face.OrientationIsReversed to be set to True, but not both. The surface normal point moved to the other side as expected. The face normal point did not move and Face.OrientationIsReversed was still false which was not expected.

So what it appears is Face.Reverse(0, True) is the ONLY method that is consistent while Brep.Flip() and Surface.Reverse() are not consistent. Is this true? What am I missing? Is this an issue / bug?

Furthermore, if I use the Surface / Brep Face to trim another object by using rs.TrimBrep, the surface normal in this function is used, not the face normal. So one must be very careful as to how they flip / reverse their object if the outcome is to use the object as a trimming object so that the surface normal is altered properly.

Also when I used brep.Flip(), the normal did not flip. How do I flip the normal when using brep, face, and surface objects?

Brep Flip Reverse.3dm (171.0 KB)
Brep Flip Reverse.py (3.0 KB)

Hi Mike - the underlying surface of a brep face gets its normals ‘naturally’ from the U and V directions and the ‘right hand rule’. A brep face though, can be flipped to show a normal direction at odds with its underlying surface. The brep face normal does not affect the underlying surface… dunno if that helps or not…

-Pascal

To add to the answer that @pascal gave, to properly obtain the normal direction of a Brep face, BrepFace.OrientationIsReversed needs to be taken into account.

Every now and then it’s worth showing this.

– Dale

5 Likes

Wow, a visual representation of the relationship between some Rhino objects. That is so-ooo great! More please.

Dale, could you briefly explain what the Loop is all about? Is there any correlation with the Edge collection of a Brep?

Thanks
Jeremy

It helps a little. I think I have more questions than answers.

1 Like

+1 Agreed. More visuals really help with the object relationships. I’ve been wondering about loops myself so I did a bit of research. According to the docs: A Brep face is composed of one surface and trimming curve and a loop is composed of a list of trim curves. I notice in the brep objects there are faces, vertices, surfaces, edges, trims, and loops. I have added some routines to the original script to help understand these. One shows the vertices Another shows the edges. Another shows the trims. And the last one shows the loops. It looks like there is always an outer loop that represents the outermost boundary of face. There can be one or more inner loops that represent the boundaries of each cut in the face. Within each loop there are trims that represent the edges of the loop. Meanwhile, the brep.Trims list is a list of ALL the trims found in all the loops. When the routine that I create draws the trims from brep.Trims or the trims from within each loop, they are drawn from 0,0 so their coordinates must be relative to the first point of the brep whereas the edges and vertices are absolute coordinates.

Dale and Pascal, please correct me if I’m wrong.

I’m attaching the new Rhino file and py file so you can run the code to see the different sub-objects. Just select the surface with the 2 holes at the top. Note the surface will be hidden after the operations. Just unhide it to get it back.

Brep Flip Reverse.3dm (193.8 KB)
Brep Flip Reverse.py (5.9 KB)

Ok one of my concerns is in paragraph 6 above where I talk about the 3rd object. You say that “A brep face though, can be flipped to show a normal direction at odds with its underlying surface. The brep face normal does not affect the underlying surface.” When I use BrepFace.Reverse(0, True) the surface’s normal changes to the other side and then BrepFace.OrientationIsReversed gets changed to the opposite value. So it appears that BOTH the surface and brep face change when using that method. So it appears that there is an issue here.

On the second object (paragraph 5) I used Brep.Flip(). The surface normal did not change so it appears that this method would only affect all the brep faces. I inspected the brep face and turns out it not only changed the normal direction but it also changed the OrientationIsReversed value to its opposite. One or the other should have been performed on the brep face, but not both. So it appears there’s an issue here.

On the last object (paragraph 7) I used Surface.Reverse(0, True). The surface normal was affect while the brep face normal did not change. This aligns with what you stated.

Hi @jeremy5,

A loop, or a parameter space trimming loop, represent a single loop in a Brep object. A loop is composed of a list of trim curves.

Loops and trims are purely topological - geometry queries should be directed at the trim’s 2d curve or the trim’s edge’s 3d curve.

Loops are always oriented so that the active region of the face is to the left of the 2D curve. Thus, outer loops are oriented counter-clockwise and inner loops are oriented clockwise.

– Dale

1 Like

Hi @Mike24,

BrepFace does not have a Reverse method.

A BrepFace is a surface proxy - it inherits from SurfaceProxy. That is, it acts on behalf of it’s underlying surface. So by calling BrepFace.Reverse, you are actually calling through the surface proxy to the underlying surface. A BrepFace is a topological object.

Hope this helps.

– Dale

@dale @pascal - What’s the difference between a brep edge and a brep trim. When I run the following routine, I get the same object. The edge is using absolute coordinates while the trim is using relative coordinates and is drawn at 0,0,0. Is that the only difference?

def ShowSurfaceFirstEdgeAndTrim():
    srf = rs.GetSurfaceObject('Select the surface object to inspect.')
    
    oSrf = rs.coercesurface(srf[0])
    # This returns a brep face object
    oBrep = oSrf.Brep
    
    rs.UnselectAllObjects()

    # Duplicate the first edge    
    oFirstEdge = oBrep.Edges[0]
    oNurbsCurve = oFirstEdge.ToNurbsCurve()
    nurbsCurve = sc.doc.Objects.Add(oNurbsCurve)
    rs.ObjectColor(nurbsCurve, rs.CreateColor(255,0,0))

    # Duplicate the first trim
    oFirstTrim = oBrep.Trims[0]
    oNurbsCurve = oFirstTrim.ToNurbsCurve()
    nurbsCurve = sc.doc.Objects.Add(oNurbsCurve)
    rs.ObjectColor(nurbsCurve, rs.CreateColor(0,255,0))
     
    rs.HideObject(srf[0])
    rs.Redraw()
    print 'Surface hidden.  First edge duplicated and is red.  First trim is duplicated and is green.  Notice that objects relative to the first point of the brep were drawn at 0,0,0.'

What’s the purpose of having a brep face normal at odds with it’s underlying surface. Why would someone want to have the normals be opposite to each other?

If you review the diagram above, you’ll see that both edges and trims are topological objects, curve proxies to their underlying 3d and 2d (parameter space) curves, respectively.

You might find it useful to walk through this sample.

test_traverse_brep.py (4.7 KB)

– Dale

Great…that helps. Thanks!

@Dale- Can you please look @ these 2 responses. I want clarification on this topic so I better understand what methods to use and how to use them. Thanks in advance.

In general, you want to avoid modifying the underlying geometry for this can invalidate the topology, requiring it to be re-computed.

I’ve already answered this (see above).

– Dale

Some posts explaining this topic:

-Kevin

1 Like

Great information!

Ok…if I have a surface that is used to trim an object, what’s the best way to flip the normals on the surface so that it trims the other side of the target object?

  1. I have tried Brep.Flip() which does not work.

  2. I have tried BrepFace.Reverse(0, True) which flips the normal.

  3. I have tried Surface.Reverse(0, True) which does not flip the normal.

So, is BrepFace.Reverse(0, True) the best way to flip normals?

Can you post a simple example that you cannot make work?

– Dale

I am posting a Rhino file and script. All the normals are pointing to the right of all 3 surface objects. The script calls BrepFace.Reverse(0, True) and proceeds to trim the brep so it should trim the left side of the brep. What’s interesting is the top 2 objects trim just fine, but the bottom object does not. It always trims to the right. The normals are never reversed on the surface object on the bottom set. The bottom object was from a 3rd party so I’m not sure how it was created. I created the top 2 object sets. At first I created a rectangular surface and it tested fine (very top set). So I thought maybe it was due to the surface being trimmed. So I copied the top surface and trimmed it. That’s the middle surface. It worked fine too so I know the surface being trimmed works as it should.

That bottom surface is the problem child. Can you please take a look at it and see if there is something about it that I’m not seeing? Thanks in advance.

I even tried rs.FlipSurface() and received the same results.

Brep Trim Test.3dm (212.2 KB)
Brep Trim Test.py (640 Bytes)