I asked ChatGPT to Flow along multiple surfaces with custom tolerance

Instead of using FlowAlongSurface with only one target, with this script, you can select several targets.
Since FlowAlongSrf might run into trouble on high tolerances, I added custom tolerance input, with the default being 0.1.

# Flow geometry from reference surface to multiple target surfaces
# Works with Meshes, Breps, Curves, Points, PointClouds, and Extrusions
# Extrusions are converted to Breps before morphing

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import System


def get_tolerance_option(default_tol=0.1):
    go = Rhino.Input.Custom.GetOption()
    go.SetCommandPrompt("Tolerance ({})".format(default_tol))
    go.AcceptNothing(True)

    opt_map = [
        ("T1_0",   1.0),
        ("T0_1",   0.1),
        ("T0_01",  0.01),
        ("T0_001", 0.001),
        ("Doc",    None),
    ]

    for name, _ in opt_map:
        go.AddOption(name)

    while True:
        res = go.Get()

        if res == Rhino.Input.GetResult.Nothing:
            return default_tol

        if res == Rhino.Input.GetResult.Option:
            idx = go.OptionIndex() - 1
            if idx < 0 or idx >= len(opt_map):
                return None

            name, val = opt_map[idx]
            return sc.doc.ModelAbsoluteTolerance if name == "Doc" else val

        return None


def get_first_surface(obj_id):
    brep = rs.coercebrep(obj_id)
    if not brep or brep.Faces.Count < 1:
        return None

    return brep.Faces[0].UnderlyingSurface()


def add_geometry_to_doc(geom, atts):
    if isinstance(geom, Rhino.Geometry.Mesh):
        geom.Normals.ComputeNormals()
        geom.Compact()
        return sc.doc.Objects.AddMesh(geom, atts)

    elif isinstance(geom, Rhino.Geometry.Brep):
        return sc.doc.Objects.AddBrep(geom, atts)

    elif isinstance(geom, Rhino.Geometry.Curve):
        return sc.doc.Objects.AddCurve(geom, atts)

    elif isinstance(geom, Rhino.Geometry.Point):
        return sc.doc.Objects.AddPoint(geom.Location, atts)

    elif isinstance(geom, Rhino.Geometry.PointCloud):
        return sc.doc.Objects.AddPointCloud(geom, atts)

    else:
        return sc.doc.Objects.Add(geom, atts)


def flow_along_srf_rhinocommon_keep_props_and_groups():
    geo_ids = rs.GetObjects(
        "Select GeometryToFlow",
        preselect=True
    )
    if not geo_ids:
        return

    rs.UnselectAllObjects()

    ref_id = rs.GetObject(
        "Select RefSurface / Base Surface",
        rs.filter.surface | rs.filter.polysurface,
        preselect=False
    )
    if not ref_id:
        return

    rs.UnselectAllObjects()

    tgt_ids = rs.GetObjects(
        "Select Target Surfaces",
        rs.filter.surface | rs.filter.polysurface,
        preselect=False
    )
    if not tgt_ids:
        return

    tol = get_tolerance_option(default_tol=0.1)
    if tol is None:
        return

    ref_srf = get_first_surface(ref_id)
    if not ref_srf:
        print("Could not read reference surface.")
        return

    src_items = []
    src_group_indices = set()

    for gid in geo_ids:
        rhobj = sc.doc.Objects.FindId(gid)
        if not rhobj or not rhobj.Geometry:
            continue

        geom = rhobj.Geometry.Duplicate()

        # Important: convert extrusions to Breps before morphing
        if isinstance(geom, Rhino.Geometry.Extrusion):
            geom = geom.ToBrep()

        atts = rhobj.Attributes.Duplicate()

        g_list = list(atts.GetGroupList()) if atts.GroupCount > 0 else []

        for gi in g_list:
            src_group_indices.add(gi)

        src_items.append((geom, atts, g_list))

    if not src_items:
        print("No valid geometry found.")
        return

    for ti, tid in enumerate(tgt_ids, start=1):
        tgt_srf = get_first_surface(tid)
        if not tgt_srf:
            print("Skipping invalid target surface.")
            continue

        morph = Rhino.Geometry.Morphs.SporphSpaceMorph(ref_srf, tgt_srf)
        morph.Tolerance = tol
        morph.PreserveStructure = False
        morph.ConstrainNormal = Rhino.Geometry.Vector3d.Unset

        group_map = {}

        for src_gi in src_group_indices:
            old_name = sc.doc.Groups.GroupName(src_gi) or "Group"
            new_name = "{}__Flow_{:02d}".format(old_name, ti)
            new_gi = sc.doc.Groups.Add(new_name)
            group_map[src_gi] = new_gi

        for geom, atts, g_list in src_items:
            dup = geom.Duplicate()
            if not dup:
                continue

            morph.Morph(dup)

            new_atts = atts.Duplicate()

            try:
                new_atts.RemoveFromAllGroups()
            except:
                pass

            new_id = add_geometry_to_doc(dup, new_atts)

            if new_id == System.Guid.Empty:
                print("Failed to add morphed object.")
                continue

            for src_gi in g_list:
                new_gi = group_map.get(src_gi)
                if new_gi is not None:
                    sc.doc.Groups.AddToGroup(new_gi, new_id)

    sc.doc.Views.Redraw()
    print("Flow complete.")


if __name__ == "__main__":
    flow_along_srf_rhinocommon_keep_props_and_groups()

This post is related to:

1 Like