MeshSplit Bug

Seems to be a refresh issue. Because of the grouping of many mesh faces. If you select on anything they are stll grouped together.

The result of MeshSplit has to stay selected, just like when surfaces are split.

@scottd , what about SubSurface splitting? Maybe to be implemented in WIP ?

# SubSurface split
import Rhino
import scriptcontext as sc
import System
from Rhino.Input.Custom import GetObject
from Rhino.DocObjects import ObjectType


def select_subfaces():
    go = GetObject()
    go.SetCommandPrompt("Select sub-surfaces (faces) of polysurfaces to split (Ctrl+Shift for faces)")
    go.GeometryFilter = ObjectType.Surface
    go.SubObjectSelect = True
    go.GroupSelect = True
    go.EnablePreSelect(True, True)
    go.EnableClearObjectsOnEntry(False)
    go.EnableUnselectObjectsOnExit(False)
    go.DeselectAllBeforePostSelect = False

    rc = go.GetMultiple(1, 0)
    if rc != Rhino.Input.GetResult.Object:
        return None

    refs = []
    for i in range(go.ObjectCount):
        r = go.Object(i)
        if r is None: 
            continue
        # must be a face reference
        if r.Face() is not None:
            refs.append(r)
    return refs


def select_cutters():
    go = GetObject()
    go.SetCommandPrompt("Select cutter surfaces/polysurfaces")
    go.GeometryFilter = ObjectType.Surface | ObjectType.PolysrfFilter
    go.SubObjectSelect = False
    go.GroupSelect = True
    go.EnablePreSelect(True, True)
    go.EnableClearObjectsOnEntry(False)
    go.EnableUnselectObjectsOnExit(False)
    go.DeselectAllBeforePostSelect = False

    rc = go.GetMultiple(1, 0)
    if rc != Rhino.Input.GetResult.Object:
        return None

    cutters = []
    for i in range(go.ObjectCount):
        r = go.Object(i)
        if r is None:
            continue

        brep = r.Brep()
        if brep is None:
            srf = r.Surface()
            if srf is not None:
                brep = srf.ToBrep()

        if brep is not None:
            cutters.append(brep)
    return cutters


def try_join(breps, tol):
    # RhinoCommon wants IEnumerable<Brep>
    if not breps:
        return None
    joined = Rhino.Geometry.Brep.JoinBreps(breps, tol)
    if joined and len(joined) > 0:
        return list(joined)
    return None


def main():
    doc = sc.doc
    tol = doc.ModelAbsoluteTolerance

    face_refs = select_subfaces()
    if not face_refs:
        print("No faces selected.")
        return

    cutter_breps = select_cutters()
    if not cutter_breps:
        print("No cutters selected.")
        return

    # Group selected faces by their parent object id, with face indices
    faces_by_obj = {}  # Guid -> set(faceIndex)
    for r in face_refs:
        obj_id = r.ObjectId
        face = r.Face()
        if face is None:
            continue
        fi = face.FaceIndex
        if obj_id not in faces_by_obj:
            faces_by_obj[obj_id] = set()
        faces_by_obj[obj_id].add(fi)

    new_ids = []
    replaced = 0
    failed = 0
    multi_out = 0

    for obj_id, face_indices in faces_by_obj.items():
        rh_obj = doc.Objects.FindId(obj_id)
        if rh_obj is None:
            failed += 1
            continue

        brep = rh_obj.Geometry
        if not isinstance(brep, Rhino.Geometry.Brep):
            failed += 1
            continue

        # Build a list of face-breps:
        # - unselected faces: keep as-is (duplicate face)
        # - selected faces: split and use resulting pieces
        parts = []
        try:
            face_count = brep.Faces.Count
        except:
            failed += 1
            continue

        for i in range(face_count):
            f = brep.Faces[i]
            face_brep = f.DuplicateFace(True)  # keeps trims
            if face_brep is None:
                continue

            if i in face_indices:
                split_pieces = face_brep.Split(cutter_breps, tol)
                if split_pieces and len(split_pieces) > 0:
                    parts.extend(list(split_pieces))
                else:
                    # no split -> keep original face
                    parts.append(face_brep)
            else:
                parts.append(face_brep)

        # Join back into polysurface (or surface)
        joined = try_join(parts, tol)

        if joined is None or len(joined) == 0:
            failed += 1
            continue

        # Best case: single joined Brep -> replace original
        if len(joined) == 1:
            new_brep = joined[0]
            ok = doc.Objects.Replace(obj_id, new_brep)
            if ok:
                replaced += 1
                new_ids.append(obj_id)  # same id after replace
            else:
                # fallback: delete+add (keeps result even if replace fails)
                attrs = rh_obj.Attributes.Duplicate()
                doc.Objects.Delete(rh_obj, True)
                gid = doc.Objects.AddBrep(new_brep, attrs)
                if gid and gid != System.Guid.Empty:
                    new_ids.append(gid)
                    replaced += 1
                else:
                    failed += 1
            continue

        # If join returns multiple Breps, we can't keep it as "one polysurface".
        # We'll delete the original and add all pieces (still valid geometry, just not one Brep).
        multi_out += 1
        attrs = rh_obj.Attributes.Duplicate()
        doc.Objects.Delete(rh_obj, True)
        for jb in joined:
            gid = doc.Objects.AddBrep(jb, attrs)
            if gid and gid != System.Guid.Empty:
                new_ids.append(gid)

    if new_ids:
        doc.Objects.UnselectAll()
        for gid in new_ids:
            doc.Objects.Select(gid, True)
        doc.Views.Redraw()

    print("Done.")
    print("  Polysurfaces processed: {}".format(len(faces_by_obj)))
    print("  Successfully replaced/updated: {}".format(replaced))
    if multi_out:
        print("  NOTE: {} object(s) produced multiple Breps after join; original was replaced by multiple objects.".format(multi_out))
    if failed:
        print("  Failures: {}".format(failed))


if __name__ == "__main__":
    main()