I almost completely automated the process. Except for _Move
What can be added in order for the “extrusion cap” to automatically moving to the “extrusion sides”?
This is almost good, it has a few glitches:
1 - It ditches the color the input polysurface had already
2 - It ditches the name the polysurface had already
3 - It also changes color/name to other things around
See here:
4 - It does not give you a pause to input a direction option if you want other than Cplane Normal.:
I’m bringing all thsi up because it would be great to have a well working one of this. I also have a macro for this but it only gets me half-way there. It still I find it unbelievable that ExtrudeSRF does not have an option to extrude as a single solid, @pascal, could you lobby to have an _extrudejoined option here in ExtrudeSrf? Right now in V6 the command it doing it wrong in two ways:
it gives you a false preview of walls between surface seams that it actually does not build.
If the macro works correctly to the end, then in the current scene around should be formed only one color of the current layer . And only one name (ExtrudePolySrf)
I do not think that before the start of the macro in your scene there will be objects with the names ExtrudeCap and color 100,170,230
Although everything is possible
Here is a macro that pauses to select the direction of extrusion
Took me almost a decade after that thread, but I finally got around to writing this script. Maybe it’ll be useful to someone — just finally found the time to do it.
_-RunPythonScript (
"""
ExtrudePolySurface
Author: leex
Date: 15.11.2025
Rhino: 8
Description:
Creates extruded solid bodies from selected surfaces and polysurfaces
Features:
- Extrude multiple objects at once
- Add top and bottom caps
- Preserve names, colors, and layers of the original objects
- Interactive selection of extrusion direction
- Works with surfaces, polysurfaces, planar and freeform geometry
"""
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import time
def get_bbox_center(obj_id):
robj = sc.doc.Objects.Find(obj_id)
if not robj: return None
geo = robj.Geometry
bb = geo.GetBoundingBox(True)
return bb.Center
def to_vec_tuple(v):
return (v.X, v.Y, v.Z)
def is_planar_surface(obj_id):
surface = rs.coercesurface(obj_id)
if not surface: return False
return surface.IsPlanar()
def get_plane_normal(obj_id):
surface = rs.coercesurface(obj_id)
if not surface: return None
if surface.IsPlanar():
success, plane = surface.TryGetPlane()
if success:
return plane.ZAxis
return None
def main():
objs = rs.GetObjects("Select surface(s)/polysurface(s)", rs.filter.surface | rs.filter.polysurface, preselect=True)
if not objs:
print("Nothing selected.")
return
original_colors = {obj: rs.ObjectColor(obj) for obj in objs}
original_layers = {obj: rs.ObjectLayer(obj) for obj in objs}
temp_objs = []
planar_objects = [obj for obj in objs if is_planar_surface(obj)]
has_planar = len(planar_objects) > 0
only_planar = len(planar_objects) == len(objs)
planar_normal = get_plane_normal(planar_objects[0]) if has_planar else None
prev_redraw = sc.doc.Views.RedrawEnabled
sc.doc.Views.RedrawEnabled = False
try:
rs.UnselectAllObjects()
rs.SelectObjects(objs)
pre_all = set(rs.AllObjects() or [])
rs.Command("_-DupBorder")
time.sleep(0.05)
post_dup = set(rs.AllObjects() or [])
dup_curves = [g for g in post_dup - pre_all if rs.IsCurve(g)]
if not dup_curves:
print("No border curves created.")
return
temp_objs.extend(dup_curves)
curve_centers = [get_bbox_center(c) for c in dup_curves if get_bbox_center(c)]
if not curve_centers:
print("Could not calculate curve centers.")
return
avg_curve_center = Rhino.Geometry.Point3d(
sum([c.X for c in curve_centers])/len(curve_centers),
sum([c.Y for c in curve_centers])/len(curve_centers),
sum([c.Z for c in curve_centers])/len(curve_centers)
)
sc.doc.Views.RedrawEnabled = True
rs.UnselectAllObjects()
rs.SelectObjects(dup_curves)
if only_planar and planar_normal:
print("Using normal for extrusion...")
cmd = "! _ExtrudeCrv _Solid=No _Direction {} {} {}".format(planar_normal.X, planar_normal.Y, planar_normal.Z)
rs.Command(cmd)
else:
print("Specify ExtrudeCrv direction now (number or click)...")
rs.Command("_-ExtrudeCrv _Solid=No")
sc.doc.Views.RedrawEnabled = False
post_extrude = set(rs.AllObjects() or [])
new_extrusions = [g for g in post_extrude - pre_all if sc.doc.Objects.Find(g) and (rs.IsBrep(g) or rs.IsSurface(g))]
if not new_extrusions:
print("Extrusion failed.")
return
temp_objs.extend(new_extrusions)
for e in new_extrusions:
if rs.IsObject(e):
rs.ObjectColor(e, original_colors[objs[0]])
rs.ObjectLayer(e, original_layers[objs[0]])
centers_after = [get_bbox_center(g) for g in new_extrusions if get_bbox_center(g)]
if not centers_after:
print("Could not calculate extrusion centers.")
return
avg_extr_center = Rhino.Geometry.Point3d(
sum([c.X for c in centers_after])/len(centers_after),
sum([c.Y for c in centers_after])/len(centers_after),
sum([c.Z for c in centers_after])/len(centers_after)
)
vector = avg_extr_center - avg_curve_center
full_vector = vector * 2
vec_tuple = to_vec_tuple(full_vector)
top_copies = rs.CopyObjects(objs)
if top_copies:
rs.MoveObjects(top_copies, vec_tuple)
temp_objs.extend(top_copies)
for t in top_copies:
if rs.IsObject(t):
rs.ObjectColor(t, original_colors[objs[0]])
rs.ObjectLayer(t, original_layers[objs[0]])
else:
top_copies = []
if only_planar:
join_list = new_extrusions + top_copies
else:
join_list = new_extrusions + list(objs) + top_copies
valid_join_list = [obj for obj in join_list if rs.IsObject(obj)]
rs.UnselectAllObjects()
if len(valid_join_list) >= 2:
rs.SelectObjects(valid_join_list)
join_result = rs.Command("_-Join")
if join_result:
print("Successfully joined all surfaces into a solid!")
else:
print("Join failed, surfaces created but not unified.")
else:
print("Not enough objects to join. Found: {}".format(len(valid_join_list)))
for c in dup_curves:
if rs.IsObject(c):
rs.DeleteObject(c)
if c in temp_objs:
temp_objs.remove(c)
print("Done. Solid with top and bottom surfaces, colors/layer preserved.")
finally:
for obj in temp_objs:
if rs.IsObject(obj):
try: rs.DeleteObject(obj)
except: pass
sc.doc.Views.RedrawEnabled = prev_redraw
sc.doc.Views.Redraw()
rs.UnselectAllObjects()
main()
)