Inset in rhinoscriptsyntax please

There is no Inset function in rhinoscriptsyntax. It doesn’t provide direct methods for insetting faces or insetting a Brep. Most of the functions there are wrappers around RhinoCommon, and this one simply isn’t exposed.

There is no direct Inset method like InsetFace or InsetFaces in RhinoCommon either.

This is a very useful tool, should be available for scripting. Thank you!

Just installed SR23 (8.23.25251.13001, 2025-09-08).
But I don’t see it…

https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.brep/insetfaces

1 Like

I asked ChatGPT to create a grasshopper component for the InsetFaces method.
It takes surfaces or polysurfaces and a list of distances.
Returns the border faces as polysurfaces, Inner faces as surfaces and also the surfaces which couldn’t be inset.

"""Inset faces with optional per-face distances (optimized).

Inputs:
    G: Breps (surface or polysurface)
    D: distance or list of distances (set D to list access for per-face values)
    C: creaseCorners (True/False)

Outputs:
    BorderFaces  - joined border ring per face
    InnerFaces   - inner inset faces
    NoInset      - original faces for which inset failed
"""

import Rhino
import scriptcontext as sc

# Normalize G to list
if G is None:
    G_list = []
elif isinstance(G, (list, tuple)):
    G_list = list(G)
else:
    G_list = [G]

# Normalize D to list of floats
def to_float_list(x):
    if x is None:
        return []
    try:
        it = iter(x)
        if isinstance(x, (str, bytes)):
            return [float(x)]
        return [float(v) for v in it]
    except TypeError:
        return [float(x)]

D_list = to_float_list(D)

BorderFaces = []
InnerFaces = []
NoInset = []

tol = sc.doc.ModelAbsoluteTolerance
angle_tol = sc.doc.ModelAngleToleranceRadians

for g in G_list:
    if g is None or not isinstance(g, Rhino.Geometry.Brep):
        continue

    face_count = g.Faces.Count
    if face_count == 0 or not D_list:
        continue

    # Per-face distance: reuse last value if fewer distances than faces
    per_face_dist = [D_list[min(i, len(D_list) - 1)] for i in range(face_count)]

    for i in range(face_count):

        dist = per_face_dist[i]
        face = g.Faces[i]

        # Original face (for fallback)
        original_face_brep = face.DuplicateFace(True)

        # Build single-face brep
        face_brep = face.DuplicateFace(True)
        if not face_brep:
            if original_face_brep:
                NoInset.append(original_face_brep)
            continue

        # Attempt inset
        inset = face_brep.InsetFaces(
            [0],       # only face in this brep
            dist,
            False,     # loose
            False,     # ignoreSeams
            C,      # creaseCorners
            tol,
            angle_tol
        )

        # If inset failed → report original face
        if not inset or inset.Faces.Count == 0:
            if original_face_brep:
                NoInset.append(original_face_brep)
            continue

        # ----------------------------------------
        # Find inner face by topology:
        # inner = face with NO boundary edges
        # (all its edges are shared by 2+ faces)
        # ----------------------------------------
        inner_index = -1

        for fi in range(inset.Faces.Count):
            f = inset.Faces[fi]
            edge_indices = f.AdjacentEdges()
            is_inner = True

            for ei in edge_indices:
                edge = inset.Edges[ei]
                # boundary edge if only one adjacent face
                if len(edge.AdjacentFaces()) <= 1:
                    is_inner = False
                    break

            if is_inner:
                inner_index = fi
                break

        if inner_index < 0:
            # no clear inner face → treat as fail
            if original_face_brep:
                NoInset.append(original_face_brep)
            continue

        # Inner face
        inner_face_brep = inset.Faces[inner_index].DuplicateFace(True)
        if inner_face_brep:
            InnerFaces.append(inner_face_brep)
        else:
            if original_face_brep:
                NoInset.append(original_face_brep)
            continue

        # Border ring = all other faces, joined
        border_parts = []
        for fi in range(inset.Faces.Count):
            if fi == inner_index:
                continue
            bf = inset.Faces[fi].DuplicateFace(True)
            if bf:
                border_parts.append(bf)

        if border_parts:
            joined = Rhino.Geometry.Brep.JoinBreps(border_parts, tol)
            if joined and len(joined) > 0:
                BorderFaces.append(joined[0])
            else:
                # fallback: individual pieces
                BorderFaces.extend(border_parts)
        else:
            # no border faces? treat as fail
            if original_face_brep:
                NoInset.append(original_face_brep)

@dale is it posssible for InsetFaces to return the inner face of the inset at index zero?
I thought it was already the case but in some tests it was not.
Thanks!