Feature request: SimplifyPlanarFaces

We have a beginning exercise on how to model a simple spiral stairs using only surface commands. Once they have done that, as an alternative we show them how to take a straight stair extrusion and Flow it around an arc to make it quickly and volumetrically.

The annoying thing about this procedure is that Flow (UDT) will make all the horizontal stair top surfaces very complex, even if they are planar. It is even worse if one makes a freeform stairs with the same procedure.

It would be great if we could post-process these objects and replace any complexified planar faces with simple trimmed planes. I have developed a script that does that, but I think this might be a useful thing to have “native”. I am thinking about adding the possibility for the script to also replace complex “should-be” cylinder surfaces with simple trimmed cylinders - but it’s a little bit more complicated than with planar surfaces.

SimplifyPlanrFaceReq.3dm (3.6 MB)
ReplaceComplexPlanarFaces.py (3.1 KB)

4 Likes

cool,
I tried for the cylinder, but I cannot get the trim to work

import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc

def create_cylinder_from_surface():

    surface_id = rs.GetObject("Select a surface to replace with a cylinder", rs.filter.surface)
    if not surface_id:
        return

    tol = rs.UnitAbsoluteTolerance()

    original_surface = rs.coercesurface(surface_id)
    if not original_surface:
        rs.MessageBox("Failed to convert selected surface.")
        return
    result, cylinder = original_surface.TryGetFiniteCylinder(tol)
    if not result:
        rs.MessageBox("Failed to create a valid cylinder from the original surface.")
        return
    
    # Convert the cylinder to a Brep
    brep_cylinder = cylinder.ToNurbsSurface()

    # Get the original surface as a Brep
    original_brep = rs.coercebrep(surface_id)
    if not original_brep:
        rs.MessageBox("Failed to convert selected surface to Brep.")
        return
    
    # Get the face of the original surface to use for trimming
    original_face = original_brep.Faces[0]  # Assuming it's the first face

    # Create the trimmed surface
    trimmed_surface = Rhino.Geometry.Brep.CreateTrimmedSurface(original_face, brep_cylinder, tol)
    
    if not trimmed_surface:
        rs.MessageBox("Failed to create the trimmed surface.")
        sc.doc.Objects.Add(brep_cylinder)
        return
    
    # Replace the original surface with the new trimmed cylinder surface
    rs.DeleteObject(surface_id)
    sc.doc.Objects.AddBrep(trimmed_surface)
    sc.doc.Views.Redraw()

create_cylinder_from_surface()

I think I found why it fails:

  • the U, V and N directions of the cylinder must match with the surface that needs to be replaced
  • the boundaries of the cylinder must match with the boundaries of the UNTRIMMED input surface.

Since the untrimmed input surface can be a partial cylinder or a whole cylinder, this means that part of the cylinder needs to be trimmed in axial direction only. My idea is that the linear edges of the untrimmed boundary edges need to be used to find the parameters on the cylindrical surface. Once the parameters are know, the surface can be split and it is know which part to keep.

The challenge is to convert the above into reliable code :slight_smile:

Annoying as it may, converting non-deformable geometry into something “nurbsy” is required for UDT commands.

– Dale

I think Mitch is aware of that.
Mitch is doing what Rhino programmers should be doing. He is converting the planar surfaces that remain planar after a deformation back to true planes.

It doesn’t look like the N direction matters. On the original surface, it needs to have the isocurves be arcs (within tolerance) in the U direction and lines(within tolerance) in V direction. If that condition is met it looks like your code works.

I had mine well along yesterday, but went out for the evening before it was finished. The following seems to work, at least for my sample object and a couple of other simple tests. Would be good to know if there are other objects that succeed or fail for additional debugging if necessary.

Edit: refactored the code somewhat and improved reporting
ReplaceComplexPlanarAndCylFaces.py (5.3 KB)

1 Like

Hi @dale -
I was not suggesting Flow do this automatically, although a switch like one now has in BooleanUnion for automatically merging coplanar faces might indeed be useful… However, I guess it might introduce more problems later, especially with history.

I was simply suggesting for now a standalone Rhino command that one could apply to objects afterward if desired, like one can with MergeAllCoplanarFaces.

Nice!

I see that you are not using the CreateTrimmedSurface method, and instead split the cylinder.

That’s right. I found that the cylinder surface doesn’t even need to be the same size, which I stated previously. But I found the CreateTrimmedSurface doesn’t seem to like surfaces that have trims on the borders and starts to do weird things.
If for example, I set the Height of the cylinder a bit larger (e.g. Cylinder.Height1 -=1) then CreateTrimmedSurface, I get:


I’ll log a bug for that.

Yes, for me that was the easiest/logical thing to do. The only hard part is finding the corresponding split piece in the list of splits that matches the original. My cheap way out was to compare surface areas and bounding boxes, I hope that covers most possibilities. I did have to put in a larger tolerance factor for the area calculation. Always possible to refine it with more tests I guess.

Yes, I understood.

Just curious, why do you want to simply this, or other, Breps? How does this help you?

Thanks,

– Dale

Just makes a much cleaner, lighter model…

1 Like

that would be a welcome addition to the UDT toolset. BUT…keep in mind every non planar surface still would have the complexity that comes from deforming a surface, so It may only help a little…

we (by we, I mean one of the devs) could likely script in detecting planar faces, deleting them and running the cap command which would essentially do what you are asking here.

It would be useful for other stuff too. For example replacing revolved planar faces (that have a seam in them) with simple plane surfaces.

Of course, well understood.

That only works if after deleting the planar faces, the openings left are planar - so you would have to do it one-by-one. But again, I was asking for a standalone tool that works like MergeAllCoplanarFaces. No need to integrate it into the UDT tools.

My script manages it quite well now.

1 Like