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.
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()
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
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.
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.
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:
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.
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.