Grasshopper/CutShtDemo¶
The post-processor for generating laser-cutting plans from ‘playing card’ model files created using Grasshopper/HouseOfCards.
# CreateCutSht.py - contents of the CreateCutsht ghpython script
# inputs
# layers - a list of strs representing cut sht layers to create in Rhino
# no_cut - geo for no_cut layer
# score - geo for score layer
# cut_1 - geo for cut_1 layer
# cut_2 - geo for cut_2 layer
# outputs
# out - console debugging output
# notes
# script currently hard coded to take in geo from only those layers reprsented
# in input.
import scriptcontext as sc
import Rhino as r
import ghutil.ghrhinodoc as ghutil
#==============================================================================
# take in all geo inputs and order them by layer name list
allItems = [no_cut,score,cut_1,cut_2]
layerNum = 0
for name in layers:
# create or fetch cut sht layers
layer = ghutil.fetch_or_create_layer_index(name)
# delete any current items on these layers
all_objects = ghutil.all_doc_objects(name)
for obj in all_objects:
r.RhinoDoc.ActiveDoc.Objects.Delete(obj, True)
# add all objects associated with layer
layerItems = allItems[layerNum]
layerNum +=1
for item in layerItems:
ghutil.add_geometry(item,layer)
# CreateSlots.py - contents of the CreateSlots ghpython script
# inputs
# array - srfs output from orderedParts gh component
# outputs
# out - console debugging output
# slots - a collection of curves for slotting parts together
import rhinoscriptsyntax as rs
import ghutil.ghrhinodoc as ghutil
import ghutil.trees as trees
import scriptcontext as sc
import Rhino as r
#=============================================================================
def find_neighbor_intersections(array,partIndex):
"""Given an array of srfs. find all intersections with a given srf.
Return a list of tuples with neighbor index and
crvs. representing intersections"""
intersections = []
numItems = len(array)
if partIndex >= numItems:
return "part index outside of array"
part = array[partIndex]
#only searches neighbors indexed higher in the array
for i in range (partIndex, numItems):
neighbor = array[i]
intersectionTest = rs.IntersectBreps(part,neighbor)
if intersectionTest != None:
intersections.append((i,intersectionTest))
# if there are no intersections alert the user
if len(intersections) == 0:
return "this part does not intersect with its neighbors"
else:
return intersections
#=============================================================================
def evaluateCrv (crv,normalizedParameter):
"""Returns a point on a curve given a normalized parameter."""
crvParam = rs.CurveParameter(crv,normalizedParameter)
crvPt = rs.EvaluateCurve(crv,crvParam)
return crvPt
#=============================================================================
def create_slots (array,partIndex):
""" Creates half-lap slots for a given part with its intersecting neighbors.
Returns a list of tuples with part index and slot curve."""
numItems = len(array)
if partIndex >= numItems:
return "part index outside of array"
part = array[partIndex]
intersections = find_neighbor_intersections(array,partIndex)
boundary = rs.DuplicateSurfaceBorder(part)
slots = []
## check intersections with the part's boundary to determine joint case
## rs.CurveCurveIntersection returns case specific lists see F1 help.
for line in intersections:
if len(line) == 1: return
intersectTest = rs.CurveCurveIntersection(line[1],boundary)
## Case 1: slot is floating in part boundary (Only works in some cases)
if intersectTest == None:
slots.append((partIndex,line[1]))
## Case 2: intersection coincedent along an edge and can't be slotted
elif intersectTest[0][0] == 2:
print "no slot needed"
## Case 3: part and neighbor have a valid connection and slot is drawn
else:
## create Current Part slot
startPoint = intersectTest[0][1]
endPoint = evaluateCrv(line[1],.5)
slot = rs.AddLine(startPoint,endPoint)
slots.append((partIndex,slot))
## create neighbor slot
testPoint = rs.CurveEndPoint(line[1])
distance = rs.Distance(startPoint,testPoint)
if distance != 0:
startPoint = testPoint
else:
startPoint = rs.CurveStartPoint(line[1])
slot = rs.AddLine(startPoint,endPoint)
slots.append((line[0],slot))
return slots
#=============================================================================
def create_all_slots (array):
"""Creates slots for all srfs. in an array. Returns a 2D list with
slots grouped by part."""
numItems = len(array)
slotCollection =[[] for i in range(numItems)]
for i in xrange (numItems):
currentSlots = create_slots(array,i)
if currentSlots != None:
for slot in currentSlots:
slotCollection[slot[0]].append(slot[1])
return slotCollection
######################################################
slots = trees.list_to_tree(create_all_slots (array))
# CreateTags.py - contents of the CreateTags ghpython script
# inputs
# array - a list of srfs representing parts
# names - a list of strs to label parts
# prefix - str to be added as prefix to string labels
# outputs
# tagLabels - a list of strs to label tags
# tagLocations - a list of points to locate tags
# Notes
# Part Array read directly from specified Rhino layer ("Cards" default).
# Part labels derived from Rhino object names.
import rhinoscriptsyntax as rs
import scriptcontext as sc
import ghutil.ghrhinodoc as ghutil
import Rhino as r
#==============================================================================
def create_tag_label(prefix,obj_name):
""" Given an object and prefix, create a tag label that combines the prefix
and Rhino object name. Return label as a string"""
lable = prefix + "_"+ obj_name
return lable
#==============================================================================
def create_tag_location (part,u,v):
"""Given a srf., create a tag location on the srf at a given normalized
u,v parameter"""
parameter = rs.SurfaceParameter(part,(u,v))
tagPt = rs.EvaluateSurface(part,parameter[0],parameter[1])
return tagPt
#==============================================================================
def tag_array (u,v,array,names):
""" Create tag labels and locations for an array of srfs.. Returns a list of
tuples with labels and locations."""
labels = []
locations = []
for i in range(len(array)):
labels.append (create_tag_label (prefix,names[i]))
locations.append (create_tag_location (array[i],.5,.5))
tags = (labels,locations)
return tags
###############################################################################
tags = tag_array (.5,.5,array,names)
labels,positions = tags[0],tags[1]
# CutShtLayout.py - contents of the CutShtLayout ghpython script
# inputs
# features - any geometry associated with parts to be placed on stock
# stockOutline - a srf defining the extent of the stock
# partSpacing - spacing btwn parts on stock (float)
# margin - offset from stock edge (float)
# outputs
# out - console debugging output
# basePlanes - a list of part base planes
# targetPlanes - a list of part target planes
# cutShtGeo - part slots and features stored in branches
# notes
# Part Array read directly from specified Rhino layer ("Cards" default).
# Part labels derived from Rhino object names.
import rhinoscriptsyntax as rs
import ghutil.ghrhinodoc as ghutil
import ghutil.trees as trees
import scriptcontext as sc
import Rhino as r
#=============================================================================
def set_part_base_plane(part):
"""returns an alinged plane at (U.5,V.5) of a planar surface"""
x = True
if x == True:#rs.IsPlaneSurface(part):
normalizedParameter = (.5,.5)
frameParameter = rs.SurfaceParameter(part,normalizedParameter)
frame = rs.SurfaceFrame(part,frameParameter)
return frame
else: return "object is not a planar surface"
#==============================================================================
def set_partAligned_boundingBox(part):
"""returns a bounding box aligned to a planar surface"""
basePlane = set_part_base_plane(part)
boundingBox = rs.BoundingBox(part,basePlane)
return boundingBox
#=============================================================================
def dimension_boundingBox(part):
"""returns a tuple with the height and width of bounding box"""
boundingBox = set_partAligned_boundingBox(part)
lineHeight = rs.AddLine(boundingBox[0],boundingBox[1])
lineWidth = rs.AddLine(boundingBox[0],boundingBox[3])
partHeight = rs.CurveLength(lineHeight)
partWidth = rs.CurveLength(lineWidth)
dimensions = (partHeight,partWidth)
return dimensions
#=============================================================================
def generate_base_planes_from_array(array):
"""returns a list of base planes aligned to objects in an array"""
basePlanes = []
for i in range(len(array)):
part = array[i]
basePlanes.append(set_part_base_plane(part))
return basePlanes
#=============================================================================
def create_cut_sht_targets(stockOutline,array,margin,partSpacing):
"""returns a list of target planes to evenly space parts on a given stock"""
numParts = len(array)
stockDimensions = dimension_boundingBox(stockOutline)
partDimensions = dimension_boundingBox(array[0])
stockHeight = stockDimensions[0]
stockWidth = stockDimensions[1]
partHeight = partDimensions[0]
partWidth = partDimensions[1]
yStartPt = partWidth/2.0 + margin
xStartPt = partHeight/2.0 + margin
ySpacing = partWidth + partSpacing
xSpacing = partHeight + partSpacing
rowWidth = stockWidth - (2*margin)
columnHeight = stockHeight - (2*margin)
currentX = xStartPt
currentY = yStartPt
locationPts = []
targetPlanes = []
for i in range (len(array)):
xLimit = currentX + (partWidth/2.0)
yLimit = currentY + (partHeight/2.0)
if yLimit > columnHeight:
print "parts do not fit on stock"
return None
elif xLimit > rowWidth:
currentY += ySpacing
currentX = xStartPt
partCenterPt = rs.AddPoint(currentX,currentY,0)
locationPts.append(partCenterPt)
targetPlane = rs.PlaneFromFrame( partCenterPt, [1,0,0], [0,1,0] )
targetPlanes.append(targetPlane)
currentX += xSpacing
else:
partCenterPt = rs.AddPoint(currentX,currentY,0)
locationPts.append(partCenterPt)
targetPlane = rs.PlaneFromFrame( partCenterPt, [1,0,0], [0,1,0] )
targetPlanes.append(targetPlane)
currentX += xSpacing
return targetPlanes
#=============================================================================
def reorient_objects(objects,basePlane,targetPlane, copy = True):
"""performs a plane to plane reorient on an object w/ or w/out copying"""
if targetPlane == None:
return None
else:
world = rs.WorldXYPlane()
xform1 = rs.XformChangeBasis(world,basePlane)
xform2 = rs.XformChangeBasis (targetPlane,world)
xform_final = rs.XformMultiply(xform2,xform1)
transform = rs.TransformObjects(objects,xform_final,copy)
return transform
def create_cut_sht(stockOutline,array,features,partSpacing,margin):
""" """
numParts = len(array)
basePlanes = generate_base_planes_from_array(array)
targetPlanes = create_cut_sht_targets(stockOutline,array,margin,partSpacing)
if targetPlanes == None:
return None
else:
# converts GH branch to python list for a set of features
features = [item for item in features.Branches]
cut_sht = []
for i in range(numParts):
objects = [array[i]]
for item in features[i]:
objects.append(item)
cutPart = reorient_objects(objects,basePlanes[i],targetPlanes[i])
cut_sht.append(cutPart)
return cut_sht
###############################################################################
a = trees.list_to_tree(generate_base_planes_from_array(array))
b = create_cut_sht_targets(stockOutline,array,margin,partSpacing)
c = trees.list_to_tree(create_cut_sht(stockOutline,array,features,partSpacing,margin))
import os,sys
# The first system path element always appears to be the folder containing the Grasshopper sketch.
# Construct a valid path to the library code, then check to see if it needs to be added to sys.path.
python_library_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(sys.path[0]))), "python")
# sys.path is persistent, so only insert the library path on the first invocation
if sys.path[1] != python_library_path:
print "Inserting python library path."
sys.path.insert(1, python_library_path)
print "sys.path is now", sys.path
# load the Grasshopper utility functions from the course packages
from ghutil import *
import ghutil.ghrhinodoc as ghutil
import ghutil.trees as trees
# OrderParts.py - contents of the OrderParts ghpython script
# inputs
# layer_name - str indicating Rhino layer name where parts are stored
# outputs
# out - console debugging output
# array - list of srfs representing part boundaries
# names - a list of strs corresponding to Rhino.DocObject Names
# notes
# Best if Rhino parts are named with sequential integers
# script reorders grasshopper list to correspond to Rhino part order and
# maintains Rhio naming convention
import rhinoscriptsyntax as rs
import ghutil.ghrhinodoc as ghutil
import ghutil.trees as trees
import scriptcontext as sc
import Rhino as r
#============================================================================
#fetch all objects on specified layer
all_objects = ghutil.all_doc_objects(layer_name)
# get and order Rhino object names
name = [obj.Name for obj in all_objects]
names = sorted(name)
geom = [obj.Geometry for obj in all_objects]
# create a list of srfs based on ordered name list
dict_tuple = zip(name,geom)
parts = dict(dict_tuple)
array = []
items = sorted(parts)
for item in items:
array.append (parts[str(item)])