Sometimes is just so much useful to directly input subsurfaces into grasshopper.
In the video I take subsurfaces of a polysurface and add different hatches to them.
And this is the code for the component:
# input subsurfaces. use buttons for pick and clear
# Inputs:
# pick : Button
# clear : Button
# Output:
# A : picked subsurfaces as Breps
import Rhino
import Rhino.DocObjects as rd
import scriptcontext as sc
import Grasshopper
comp = ghenv.Component
ghdoc = comp.OnPingDocument()
# ------------------------------------------------------------
# Persistent state per component instance
# ------------------------------------------------------------
key = "SubSrfPicker_" + str(comp.InstanceGuid)
state = sc.sticky.get(key)
if state is None:
state = {
"refs": [], # list of (Guid, face_index)
"pick_was_down": False,
"clear_was_down": False,
"subscribed": False,
"scheduled": False
}
sc.sticky[key] = state
# ------------------------------------------------------------
# Helper: schedule ONE recompute safely
# ------------------------------------------------------------
def schedule_recompute():
st = sc.sticky.get(key)
if st is None:
return
if st.get("scheduled", False):
return
st["scheduled"] = True
sc.sticky[key] = st
doc = comp.OnPingDocument()
if doc is None:
st["scheduled"] = False
sc.sticky[key] = st
return
def cb(d):
st2 = sc.sticky.get(key)
if st2 is not None:
st2["scheduled"] = False
sc.sticky[key] = st2
comp.ExpireSolution(False)
doc.ScheduleSolution(10, cb)
# ------------------------------------------------------------
# Rhino doc event handlers
# ------------------------------------------------------------
def on_replace(sender, e):
st = sc.sticky.get(key)
if st is None:
return
watched_ids = set([obj_id for obj_id, fi in st["refs"]])
if e.ObjectId in watched_ids:
schedule_recompute()
def on_delete(sender, e):
st = sc.sticky.get(key)
if st is None:
return
watched_ids = set([obj_id for obj_id, fi in st["refs"]])
if e.ObjectId in watched_ids:
schedule_recompute()
def on_undelete(sender, e):
st = sc.sticky.get(key)
if st is None:
return
watched_ids = set([obj_id for obj_id, fi in st["refs"]])
if e.ObjectId in watched_ids:
schedule_recompute()
# ------------------------------------------------------------
# Subscribe once per component instance
# ------------------------------------------------------------
if not state.get("subscribed", False):
Rhino.RhinoDoc.ReplaceRhinoObject += on_replace
Rhino.RhinoDoc.DeleteRhinoObject += on_delete
Rhino.RhinoDoc.UndeleteRhinoObject += on_undelete
state["subscribed"] = True
sc.sticky[key] = state
# ------------------------------------------------------------
# Clear logic
# If one or more stored subsurfaces are currently selected in Rhino,
# remove only those.
# If none are selected, clear all.
# Rising-edge detect on clear button
# ------------------------------------------------------------
if clear and not state.get("clear_was_down", False):
state["clear_was_down"] = True
refs_to_remove = []
for obj_id, face_index in state["refs"]:
rh_obj = Rhino.RhinoDoc.ActiveDoc.Objects.FindId(obj_id)
if rh_obj is None:
continue
ci = Rhino.Geometry.ComponentIndex(
Rhino.Geometry.ComponentIndexType.BrepFace,
face_index
)
if rh_obj.IsSubObjectSelected(ci):
refs_to_remove.append((obj_id, face_index))
if refs_to_remove:
remove_set = set(refs_to_remove)
state["refs"] = [r for r in state["refs"] if r not in remove_set]
else:
state["refs"] = []
sc.sticky[key] = state
schedule_recompute()
elif not clear and state.get("clear_was_down", False):
state["clear_was_down"] = False
sc.sticky[key] = state
# ------------------------------------------------------------
# Async picker callback
# Runs after the GH solution finishes
# ------------------------------------------------------------
def launch_picker(doc):
go = Rhino.Input.Custom.GetObject()
go.SetCommandPrompt("Select Brep/Extrusion face(s)")
go.SubObjectSelect = True
go.GeometryFilter = rd.ObjectType.Surface
go.GetMultiple(1, 0)
picked_refs = []
if go.CommandResult() == Rhino.Commands.Result.Success:
for i in range(go.ObjectCount):
objref = go.Object(i)
if objref is None:
continue
face = objref.Face()
if face is None:
continue
picked_refs.append((objref.ObjectId, face.FaceIndex))
st = sc.sticky.get(key, {
"refs": [],
"pick_was_down": False,
"clear_was_down": False,
"subscribed": True,
"scheduled": False
})
existing = set(st["refs"])
for ref in picked_refs:
if ref not in existing:
st["refs"].append(ref)
existing.add(ref)
st["pick_was_down"] = False
sc.sticky[key] = st
schedule_recompute()
# ------------------------------------------------------------
# Rising-edge detect on pick input
# Use a Button, not a Toggle
# ------------------------------------------------------------
if pick and not state["pick_was_down"]:
state["pick_was_down"] = True
sc.sticky[key] = state
if ghdoc is not None:
ghdoc.ScheduleSolution(1, launch_picker)
elif not pick and state["pick_was_down"]:
state["pick_was_down"] = False
sc.sticky[key] = state
# ------------------------------------------------------------
# Resolve stored refs into standalone face Breps
# Supports Breps and Extrusions
# ------------------------------------------------------------
out = []
for obj_id, face_index in state["refs"]:
rh_obj = Rhino.RhinoDoc.ActiveDoc.Objects.FindId(obj_id)
if rh_obj is None:
continue
geom = rh_obj.Geometry
brep = None
if isinstance(geom, Rhino.Geometry.Brep):
brep = geom
elif isinstance(geom, Rhino.Geometry.Extrusion):
brep = geom.ToBrep()
if brep is None:
continue
if 0 <= face_index < brep.Faces.Count:
face_brep = brep.Faces[face_index].DuplicateFace(False)
if face_brep is not None:
out.append(face_brep)
A = out