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)])