Hello, The code below will import a mesh and create a line through the mesh. I now want to twist the mesh using the line as the twist axis. But all of my attempts have failed. If anyone could give me some guidance on how to do this I would be grateful!
import os
import rhinoscriptsyntax as rs
import random
def import_random_mesh_and_process():
print("Step 1: Starting script.")
# Path to your folder containing OBJ files
folder_path = r"C:\Users\jdm037\Desktop\rhino_python_scripts\meshes_simple"
# Delete all visible geometry in the scene
print("Step 2: Deleting all visible geometry.")
visible_objects = rs.ObjectsByType(0, True, True) # 0 = all objects, include_hidden=False, include_lights=True
if visible_objects:
rs.DeleteObjects(visible_objects)
print("Step 2: All visible geometry deleted.")
else:
print("Step 2: No visible geometry to delete.")
# Get a list of all .OBJ files in the folder
print("Step 3: Gathering .OBJ files.")
mesh_files = [f for f in os.listdir(folder_path) if f.endswith('.obj')]
if not mesh_files:
print("Step 3: No OBJ files found in the specified folder.")
return
print("Step 3: OBJ files found.")
# Select a random OBJ file
print("Step 4: Selecting a random OBJ file.")
random_mesh_file = random.choice(mesh_files)
mesh_file_path = os.path.join(folder_path, random_mesh_file)
print("Step 4: Selected file - {}".format(random_mesh_file))
# Import the selected OBJ file into Rhino
print("Step 5: Importing the selected OBJ file.")
try:
rs.Command('-Import "{}" _Enter'.format(mesh_file_path))
print("Step 5: Successfully imported - {}".format(random_mesh_file))
except Exception as e:
print("Step 5: Failed to import mesh: {}".format(e))
return
# Find the longest dimension of the imported mesh and create a line
print("Step 6: Processing the imported mesh.")
try:
# Get the imported mesh
imported_mesh = rs.LastCreatedObjects(select=False)
if not imported_mesh:
print("Step 6: No mesh was imported.")
return
print("Step 6: Imported mesh detected.")
imported_mesh_id = imported_mesh[0]
bounding_box = rs.BoundingBox(imported_mesh_id)
if not bounding_box:
print("Step 6: Failed to calculate bounding box for the imported mesh.")
return
print("Step 6: Bounding box calculated.")
# Calculate the center of the bounding box
center_x = (bounding_box[0][0] + bounding_box[6][0]) / 2
center_y = (bounding_box[0][1] + bounding_box[6][1]) / 2
center_z = (bounding_box[0][2] + bounding_box[6][2]) / 2
center = (center_x, center_y, center_z)
# Calculate dimensions along X, Y, Z
x_length = abs(bounding_box[1][0] - bounding_box[0][0])
y_length = abs(bounding_box[3][1] - bounding_box[0][1])
z_length = abs(bounding_box[4][2] - bounding_box[0][2])
print("Step 6: Dimensions calculated.")
# Determine the longest axis
longest_length = max(x_length, y_length, z_length)
axis = ["X", "Y", "Z"][ [x_length, y_length, z_length].index(longest_length) ]
# Create a line along the longest axis, centered on the mesh
if axis == "X":
line_start = (center[0] - longest_length, center[1], center[2])
line_end = (center[0] + longest_length, center[1], center[2])
elif axis == "Y":
line_start = (center[0], center[1] - longest_length, center[2])
line_end = (center[0], center[1] + longest_length, center[2])
else: # Z
line_start = (center[0], center[1], center[2] - longest_length)
line_end = (center[0], center[1], center[2] + longest_length)
rs.AddLine(line_start, line_end)
print("Step 7: Created a line along the {}-axis through the center.".format(axis))
except Exception as e:
print("Step 7: Failed to process mesh: {}".format(e))
# Run the function
import_random_mesh_and_process()
I don’t see anything resembling twist in the rhinoscriptsyntax
so if anyone else finds one, it may be a nifty shortcut. Until then you can probably do something like this:
# assume you already have the variables defined as "your_...."
import Rhino.Geometry as rhg
morph = rhg.Morphs.TwistSpaceMorph()
morph.TwistAngleRadians = your_angle
morph.TwistAxis = your_line
morph.Morph(your_mesh)
Thanks, Will! Since posting this question I found a solution, but it’s a bit complicated. I will test your approach out to see how it compares. I’ll post my full code below in case it’s interesting to anyone.
import os
import rhinoscriptsyntax as rs
import random
import Rhino
import scriptcontext as sc
def import_random_mesh_and_process():
print("Step 1: Starting script.")
folder_path = r"C:\Users\jdm037\Desktop\rhino_python_scripts\meshes_simple"
print("Step 2: Deleting all visible geometry.")
visible_objects = rs.ObjectsByType(0, True, True)
if visible_objects:
rs.DeleteObjects(visible_objects)
print("Step 2: All visible geometry deleted.")
else:
print("Step 2: No visible geometry to delete.")
print("Step 3: Gathering .OBJ files.")
mesh_files = [f for f in os.listdir(folder_path) if f.endswith('.obj')]
if not mesh_files:
print("Step 3: No OBJ files found in the specified folder.")
return
print("Step 4: Selecting a random OBJ file.")
random_mesh_file = random.choice(mesh_files)
mesh_file_path = os.path.join(folder_path, random_mesh_file)
print("Step 4: Selected file - {}".format(random_mesh_file))
print("Step 5: Importing the selected OBJ file.")
try:
rs.Command('-Import "{}" _Enter'.format(mesh_file_path))
print("Step 5: Successfully imported - {}".format(random_mesh_file))
except Exception as e:
print("Step 5: Failed to import mesh: {}".format(e))
return
print("Step 6: Processing the imported mesh.")
try:
imported_mesh = rs.LastCreatedObjects(select=False)
if not imported_mesh:
print("Step 6: No mesh was imported.")
return
imported_mesh_id = imported_mesh[0]
bounding_box = rs.BoundingBox(imported_mesh_id)
if not bounding_box:
print("Step 6: Failed to calculate bounding box for the imported mesh.")
return
center_x = (bounding_box[0][0] + bounding_box[6][0]) / 2
center_y = (bounding_box[0][1] + bounding_box[6][1]) / 2
center_z = (bounding_box[0][2] + bounding_box[6][2]) / 2
x_length = abs(bounding_box[1][0] - bounding_box[0][0])
y_length = abs(bounding_box[3][1] - bounding_box[0][1])
z_length = abs(bounding_box[4][2] - bounding_box[0][2])
longest_length = max(x_length, y_length, z_length)
axis = ["X", "Y", "Z"][ [x_length, y_length, z_length].index(longest_length) ]
if axis == "X":
line_start = (center_x - longest_length, center_y, center_z)
line_end = (center_x + longest_length, center_y, center_z)
elif axis == "Y":
line_start = (center_x, center_y - longest_length, center_z)
line_end = (center_x, center_y + longest_length, center_z)
else:
line_start = (center_x, center_y, center_z - longest_length)
line_end = (center_x, center_y, center_z + longest_length)
print("Step 6: Twist axis from {} to {}".format(line_start, line_end))
rs.AddLine(line_start, line_end)
print("Step 8: Applying twist using RhinoCommon.")
mesh = rs.coercegeometry(imported_mesh_id)
if not mesh:
print("RhinoCommon fallback: Failed to coerce geometry.")
return
# Create twisting effect
twist_angle = 360
line = Rhino.Geometry.Line(
Rhino.Geometry.Point3d(*line_start),
Rhino.Geometry.Point3d(*line_end)
)
twist_center = line.PointAt(0.5)
def twist_deform(pt):
"""
Custom deformation function for twisting.
"""
t = line.ClosestParameter(pt)
angle = twist_angle * t
axis = line.UnitTangent
rotation = Rhino.Geometry.Transform.Rotation(
Rhino.RhinoMath.ToRadians(angle),
axis,
twist_center
)
pt.Transform(rotation)
return pt
twisted_mesh = mesh.Duplicate()
for i in range(twisted_mesh.Vertices.Count):
vertex = twisted_mesh.Vertices[i]
pt = twist_deform(Rhino.Geometry.Point3d(vertex.X, vertex.Y, vertex.Z))
twisted_mesh.Vertices.SetVertex(i, pt.X, pt.Y, pt.Z)
sc.doc.Objects.Replace(imported_mesh_id, twisted_mesh)
sc.doc.Views.Redraw()
print("Twist successfully applied using RhinoCommon.")
except Exception as e:
print("Step 8: Failed to process mesh: {}".format(e))
# Run the function
import_random_mesh_and_process()
Will,
By the way, every time I run this code the multipipe radius is the same, even though I’ve tried to define the radius randomly. My impression is it’s this line that’s failing:
rs.Command(‘_-MultiPipe _Radius={} _Enter _Enter’.format(pipe_radius))
Do you have any suggestions?
Thanks,
Joe
import rhinoscriptsyntax as rs
import random
import scriptcontext as sc
import Rhino
def import_random_mesh_and_create_multipipe():
print("Starting the MultiPipe script...")
# Path to your folder containing OBJ files
folder_path = r"C:\Users\jdm037\Desktop\rhino_python_scripts\meshes_simple"
pipe_diameter_percentage = 0.001 # Set the pipe diameter as a percentage of the largest bounding box dimension
print("Pipe diameter percentage: {}".format(pipe_diameter_percentage))
# Get a list of all .OBJ files in the folder
mesh_files = [f for f in os.listdir(folder_path) if f.endswith('.obj')]
print("Found {} OBJ files: {}".format(len(mesh_files), mesh_files))
if not mesh_files:
print("No OBJ files found in the specified folder. Exiting script.")
return
# Select a random OBJ file
random_mesh_file = random.choice(mesh_files)
mesh_file_path = os.path.join(folder_path, random_mesh_file)
print("Randomly selected mesh file: {}".format(random_mesh_file))
# Import the selected OBJ file into Rhino
try:
print("Attempting to import the OBJ file...")
rs.Command('-Import "{}" _Enter'.format(mesh_file_path))
print("Successfully imported random mesh: {}".format(random_mesh_file))
except Exception as e:
print("Failed to import mesh: {}".format(e))
return
# Get the most recently added object (the imported mesh)
imported_objects = rs.LastCreatedObjects()
print("Imported object IDs: {}".format(imported_objects))
if not imported_objects:
print("No objects were imported. Exiting script.")
return
# Check if the object is a mesh
mesh_id = imported_objects[0] # Assuming the first imported object is the mesh
print("Processing object ID: {}".format(mesh_id))
if rs.IsMesh(mesh_id):
print("The imported object is confirmed to be a mesh.")
# Calculate the bounding box of the mesh
bbox = rs.BoundingBox(mesh_id)
if not bbox:
print("Failed to calculate bounding box. Exiting script.")
return
print("Bounding box of the mesh: {}".format(bbox))
# Calculate the maximum dimension of the mesh
max_dimension = max(
rs.Distance(bbox[0], bbox[1]),
rs.Distance(bbox[0], bbox[3]),
rs.Distance(bbox[0], bbox[4])
)
print("Mesh maximum dimension: {}".format(max_dimension))
# Correctly calculate the pipe radius
pipe_radius = pipe_diameter_percentage * max_dimension
print("Calculated pipe radius: {:.3f}".format(pipe_radius))
# Extract the wireframe (mesh edges as polylines)
print("Extracting wireframe from the mesh...")
mesh_obj = sc.doc.Objects.Find(mesh_id)
if not mesh_obj:
print("Failed to retrieve the mesh object. Exiting script.")
return
mesh_geometry = mesh_obj.Geometry
if not isinstance(mesh_geometry, Rhino.Geometry.Mesh):
print("The object is not a valid mesh. Exiting script.")
return
edges = mesh_geometry.TopologyEdges
print("Mesh has {} topology edges.".format(edges.Count))
edge_curves = []
for i in range(edges.Count):
curve = edges.EdgeLine(i).ToNurbsCurve()
if curve:
edge_curves.append(curve)
print("Extracted {} edge curves.".format(len(edge_curves)))
# Add curves to Rhino document and store them
print("Adding curves to the Rhino document...")
curve_ids = []
for curve in edge_curves:
curve_id = sc.doc.Objects.AddCurve(curve)
curve_ids.append(curve_id)
print("Added {} curves to the document.".format(len(curve_ids)))
sc.doc.Views.Redraw()
# Ensure the curves are selected
rs.UnselectAllObjects()
rs.SelectObjects(curve_ids)
print("Selected {} curves for MultiPipe.".format(len(curve_ids)))
# Explicitly set the radius interactively
print("Executing _MultiPipe command interactively...")
rs.Command('_-MultiPipe _Radius={} _Enter _Enter'.format(pipe_radius))
print("MultiPipe created successfully. Check your Rhino workspace.")
else:
print("The imported object is not a mesh. Exiting script.")
# Run the function
import_random_mesh_and_create_multipipe()
type or paste code here