update: tested on both closed and open rails
import Rhino.Geometry as rg
# Inputs:
# rail : Curve
# profiles : list of Curves
# segments : Integer
#
# Outputs:
# breps
# report
breps = []
report = ""
tol = 0.001
angle_tol = 0.01
def profile_center(crv):
return crv.GetBoundingBox(True).Center
def make_sweep():
sw = rg.SweepOneRail()
sw.SweepTolerance = tol
sw.AngleToleranceRadians = angle_tol
sw.ClosedSweep = False
return sw
if not rail:
report = "No rail."
elif not profiles:
report = "No profiles."
else:
rail_crv = rail.DuplicateCurve()
valid_profiles = [p for p in profiles if p and isinstance(p, rg.Curve)]
if not valid_profiles:
report = "No valid profiles."
else:
sweep = make_sweep()
# CASE 1: OPEN RAIL + ONE PROFILE
if len(valid_profiles) == 1 and not rail_crv.IsClosed:
base_profile = valid_profiles[0]
rc, base_t = rail_crv.ClosestPoint(profile_center(base_profile))
if not rc:
report = "Could not find profile position on rail."
else:
rc_base, base_plane = rail_crv.PerpendicularFrameAt(base_t)
rc0, plane0 = rail_crv.PerpendicularFrameAt(rail_crv.Domain.T0)
rc1, plane1 = rail_crv.PerpendicularFrameAt(rail_crv.Domain.T1)
if not rc_base or not rc0 or not rc1:
report = "Could not get rail frames."
else:
p0 = base_profile.DuplicateCurve()
p1 = base_profile.DuplicateCurve()
p0.Transform(rg.Transform.PlaneToPlane(base_plane, plane0))
p1.Transform(rg.Transform.PlaneToPlane(base_plane, plane1))
result = sweep.PerformSweep(rail_crv, [p0, p1])
if result:
breps = list(result)
report = "Created full open sweep from one profile."
else:
report = "Open sweep failed."
# CASE 2: CLOSED RAIL + ONE PROFILE
elif len(valid_profiles) == 1 and rail_crv.IsClosed:
if not segments or segments < 3:
segments = 24
base_profile = valid_profiles[0]
rc, base_t = rail_crv.ClosestPoint(profile_center(base_profile))
if rc:
rail_crv.ChangeClosedCurveSeam(base_t)
params = rail_crv.DivideByCount(segments, False)
params = list(params) if params else []
params.insert(0, rail_crv.Domain.T0)
params.append(rail_crv.Domain.T1)
rc, base_plane = rail_crv.PerpendicularFrameAt(params[0])
temp_breps = []
if rc:
for i in range(len(params) - 1):
t0 = params[i]
t1 = params[i + 1]
seg = rail_crv.Trim(t0, t1)
if not seg:
continue
rc0, plane0 = rail_crv.PerpendicularFrameAt(t0)
rc1, plane1 = rail_crv.PerpendicularFrameAt(t1)
if not rc0 or not rc1:
continue
p0 = base_profile.DuplicateCurve()
p1 = base_profile.DuplicateCurve()
p0.Transform(rg.Transform.PlaneToPlane(base_plane, plane0))
p1.Transform(rg.Transform.PlaneToPlane(base_plane, plane1))
result = sweep.PerformSweep(seg, [p0, p1])
if result:
temp_breps.extend(list(result))
if temp_breps:
joined = rg.Brep.JoinBreps(temp_breps, tol)
breps = list(joined) if joined else temp_breps
report = "Created closed sweep from one profile."
else:
report = "Closed sweep failed."
# CASE 3: MULTIPLE PROFILES
else:
profile_data = []
for p in valid_profiles:
rc, t = rail_crv.ClosestPoint(profile_center(p))
if rc:
profile_data.append((t, p))
profile_data.sort(key=lambda x: x[0])
if len(profile_data) < 2:
report = "Need at least 2 valid profiles."
else:
temp_breps = []
for i in range(len(profile_data) - 1):
t0, p0 = profile_data[i]
t1, p1 = profile_data[i + 1]
seg = rail_crv.Trim(t0, t1)
if not seg:
continue
result = sweep.PerformSweep(seg, [p0, p1])
if result:
temp_breps.extend(list(result))
if rail_crv.IsClosed:
t0, p0 = profile_data[-1]
t1, p1 = profile_data[0]
rail_loop = rail_crv.DuplicateCurve()
rail_loop.ChangeClosedCurveSeam(t0)
rc, new_t1 = rail_loop.ClosestPoint(profile_center(p1))
if rc:
seg = rail_loop.Trim(rail_loop.Domain.T0, new_t1)
if seg:
result = sweep.PerformSweep(seg, [p0, p1])
if result:
temp_breps.extend(list(result))
if temp_breps:
joined = rg.Brep.JoinBreps(temp_breps, tol)
breps = list(joined) if joined else temp_breps
report = "Created sweep from multiple profiles."
else:
report = "Sweep failed."