Esempio n. 1
0
def surface_border_kinks(surface_guid):
    kinks = []
    borders = surface_borders(surface_guid)
    for curve_guid in borders:
        start_tgt = rs.CurveTangent(curve_guid,
                                    rs.CurveParameter(curve_guid, 0))
        end_tgt = rs.CurveTangent(curve_guid, rs.CurveParameter(curve_guid, 1))
        if not rs.IsCurveClosed(curve_guid) or not rs.IsVectorParallelTo(
                start_tgt, end_tgt):
            start = rs.CurveStartPoint(curve_guid)
            end = rs.CurveEndPoint(curve_guid)
            if start not in kinks:
                kinks.append(start)
            if end not in kinks:
                kinks.append(end)
    return kinks
Esempio n. 2
0
    def kinks(self, threshold=1e-3):
        """Return the XYZ coordinates of kinks, i.e. tangency discontinuities, along the surface's boundaries.

        Returns
        -------
        list
            The list of XYZ coordinates of surface boundary kinks.

        """
        kinks = []
        borders = self.borders(type=0)

        for border in borders:
            border = RhinoCurve(border)
            extremities = map(
                lambda x: rs.EvaluateCurve(border.guid,
                                           rs.CurveParameter(border.guid, x)),
                [0., 1.])

            if border.is_closed():
                start_tgt, end_tgt = border.tangents(extremities)
                if angle_vectors(start_tgt, end_tgt) > threshold:
                    kinks += extremities

            else:
                kinks += extremities

        return list(set(kinks))
Esempio n. 3
0
 def transf_t(t, r, s):
     plane = rh.CurvePerpFrame(path, rh.CurveParameter(path, t))
     xform = rh.XformChangeBasis(plane, geo.Plane.WorldXY)
     xform = rh.XformMultiply(xform, rh.XformScale(s))
     xform = rh.XformMultiply(
         xform, geo.Transform.Rotation(r, geo.Vector3d(0, 0, 1), rawu0))
     return rh.TransformObject(profile, xform, True)
Esempio n. 4
0
def offsetLine(line, dist):
    norm = rs.VectorRotate(rs.CurveTangent(line, rs.CurveParameter(line, 0)),
                           90, [0, 0, 1])
    norm = rs.VectorScale(rs.VectorUnitize(norm), dist)
    sideStPt = rs.VectorAdd(rs.CurveStartPoint(line), norm)
    sideEndPt = rs.VectorAdd(rs.CurveEndPoint(line), norm)
    newLine = rs.AddLine(sideStPt, sideEndPt)
    return newLine
Esempio n. 5
0
def ColorBySize():
    try:
        objs = rs.GetObjects("Select objects to color",
                             1073815613,
                             preselect=True)
        if objs is None: return
        print "Select First Color"
        firstColor = rs.GetColor()
        if firstColor is None: return
        print "Select Second Color"
        secondColor = rs.GetColor(firstColor)
        if secondColor is None: return

        rs.EnableRedraw(False)

        colorLine = rs.AddLine(firstColor, secondColor)

        areas = []
        for obj in objs:
            if rs.IsCurve(obj):
                if rs.IsCurveClosed(obj):
                    areas.append(rs.CurveArea(obj)[0])
                else:
                    areas.append(rs.CurveLength(obj))
            elif rs.IsSurface(obj):
                areas.append(rs.SurfaceArea(obj)[0])
            elif rs.IsPolysurface(obj):
                if rs.IsPolysurfaceClosed(obj):
                    areas.append(rs.SurfaceVolume(obj)[0])
            elif rs.IsHatch(obj):
                areas.append(rs.Area(obj))
            else:
                print "Only curves, hatches, and surfaces supported"
                return

        newAreas = list(areas)
        objAreas = zip(newAreas, objs)
        objAreas.sort()
        objSorted = [objs for newAreas, objs in objAreas]

        areas.sort()
        normalParams = utils.RemapList(areas, 0, 1)

        colors = []
        for t in normalParams:
            param = rs.CurveParameter(colorLine, t)
            colors.append(rs.EvaluateCurve(colorLine, param))

        for i, obj in enumerate(objSorted):
            rs.ObjectColor(obj, (colors[i].X, colors[i].Y, colors[i].Z))

        rs.DeleteObject(colorLine)
        rs.EnableRedraw(True)
        return True
    except:
        return False
def automated_smoothing_constraints(mesh, points = None, curves = None, surface = None, mesh2 = None):
	"""Apply automatically point, curve and surface constraints to the vertices of a mesh to smooth.

	Parameters
	----------
	mesh : Mesh
		The mesh to apply the constraints to for smoothing.
	points : list
		List of XYZ coordinates on which to constrain mesh vertices. Default is None.
	curves : list
		List of Rhino curve guids on which to constrain mesh vertices. Default is None.
	surface : Rhino surface guid
		A Rhino surface guid on which to constrain mesh vertices. Default is None.
	mesh2 : Rhino mesh guid
		A Rhino mesh guid on which to constrain mesh vertices. Default is None.

	Returns
	-------
	constraints : dict
		A dictionary of mesh constraints for smoothing as vertex keys pointing to point, curve or surface objects.

	"""

	if surface:
		surface = RhinoSurface.from_guid(surface)
	if curves:
		curves = [RhinoCurve.from_guid(curve) for curve in curves]
	if mesh2:
		mesh2 = RhinoMesh.from_guid(mesh2)

	constraints = {}
	constrained_vertices = {}

	vertices = list(mesh.vertices())
	vertex_coordinates = [mesh.vertex_coordinates(vkey) for vkey in mesh.vertices()]
	
	if points is not None and len(points) != 0:
		constrained_vertices.update({vertices[closest_point_in_cloud(rs.PointCoordinates(point), vertex_coordinates)[2]]: point for point in points})

	if mesh2 is not None:
		constraints.update({vkey: mesh2.guid for vkey in mesh.vertices()})

	if surface is not None:
		constraints.update({vkey: surface.guid for vkey in mesh.vertices()})
	
	if curves is not None and len(curves) != 0:
		boundaries = [split_boundary for boundary in mesh.boundaries() for split_boundary in list_split(boundary, [boundary.index(vkey) for vkey in constrained_vertices.keys() if vkey in boundary])]
		boundary_midpoints = [Polyline([mesh.vertex_coordinates(vkey) for vkey in boundary]).point(t = .5) for boundary in boundaries]
		curve_midpoints = [rs.EvaluateCurve(curve, rs.CurveParameter(curve, .5)) for curve in curves]
		midpoint_map = {i: closest_point_in_cloud(boundary_midpoint, curve_midpoints)[2] for i, boundary_midpoint in enumerate(boundary_midpoints)}
		constraints.update({vkey: curves[midpoint_map[i]].guid for i, boundary in enumerate(boundaries) for vkey in boundary})
	
	if points is not None:
		constraints.update(constrained_vertices)

	return constraints
def Main():
    mesh = rs.GetObject("please select mesh", rs.filter.mesh)
    srcs = rs.GetObjects("please select paths", rs.filter.curve)
    end = rs.MeshAreaCentroid(mesh)
    length = 6
    ang = 20
    gen = 12
    for i in range(len(srcs)):
        nParam = r.random()
        start = rs.EvaluateCurve(srcs[i], rs.CurveParameter(srcs[i], nParam))
        vec = rs.VectorCreate(end, start)
        vec = rs.VectorUnitize(vec)
        vec = vec * length
        tree = vine(mesh, start, vec, ang)
        for n in range(gen):
            tree.grow()
Esempio n. 8
0
def ColorObjsWithGradient2Pt():
    result = True
    try:
        objs = rs.GetObjects("Select objects to color",
                             1073750077,
                             preselect=True)
        if objs is None: return
        pt1 = rs.GetPoint("Select first color point")
        if pt1 is None: return
        firstColor = rs.GetColor()
        if firstColor is None: return
        pt2 = rs.GetPoint("Select second color point")
        if pt2 is None: return
        secondColor = rs.GetColor(firstColor)
        if secondColor is None: return

        rs.EnableRedraw(False)
        origLine = rs.AddLine(pt1, pt2)
        colorLine = rs.AddLine(firstColor, secondColor)

        try:
            for obj in objs:
                bboxpts = rs.BoundingBox(obj)
                ctrPt = (bboxpts[0] + bboxpts[6]) / 2
                param = rs.CurveClosestPoint(origLine, ctrPt)
                normParam = rs.CurveNormalizedParameter(origLine, param)
                colorParam = rs.CurveParameter(colorLine, normParam)
                finalPt = rs.EvaluateCurve(colorLine, colorParam)
                color = (finalPt.X, finalPt.Y, finalPt.Z)
                rs.ObjectColor(obj, color)
        except:
            result = False
        rs.DeleteObject(colorLine)
        rs.DeleteObject(origLine)
        rs.EnableRedraw(True)
    except:
        result = False
    utils.SaveFunctionData(
        'colors-Gradient',
        [firstColor, secondColor, len(objs), result])
    return result
Esempio n. 9
0
def sweepVolume(crv, tool_id, z_pos):

    tangent = rs.CurveTangent(crv, rs.CurveParameter(crv, 0))
    origin = rs.CurveStartPoint(crv)
    
    block = rs.InsertBlock( tool_id, (0,0,0), scale=(1,1,1) )
    

    # rs.DeleteObjects(objs)
       
    # pt2 = [origin.X, origin.Y + perp.XAxis[1], origin.Z]
    pt2 = [origin.X, origin.Y , origin.Z + 1]
    pt3 = [origin.X + tangent.X, origin.Y + tangent.Y , origin.Z + tangent.Z]

    ref     = [(0,0,0),(0,1,0),(0,0,1)] 
    target  = [origin, pt2 ,pt3]
    
    
    block = rs.OrientObject(block, ref, target)
    
    objs = rs.ExplodeBlockInstance(block)
    profile = None
    for item in objs:
        if rs.ObjectLayer(item) == 'HULP::C_Toolcontours' or rs.ObjectLayer(item) == 'Hulp::C_Toolcontours':
            profile = rs.CopyObject(item)
            
    
    rs.DeleteObjects(objs)


    
    if not profile:
        rs.MessageBox('there is no layer named "C_Toolcontours" in block %s' % rs.BlockInstanceName(block))
        return False
            
    profile = rs.OffsetCurve(profile, rs.CurveAreaCentroid(profile)[0], 0.001, style=1)
    
    # rs.MoveObject(profile, (0,0,z_pos))
            
    
    # rail = obj
    # rail_crv = rs.coercecurve(rail)
    # if not rail_crv: return
    # 
    # cross_sections = [profile]
    # if not cross_sections: return
    # cross_sections = [rs.coercecurve(crv) for crv in cross_sections]
    # 
    # sweep = Rhino.Geometry.SweepOneRail()
    # sweep.AngleToleranceRadians = scriptcontext.doc.ModelAngleToleranceRadians
    # sweep.ClosedSweep = True
    # # sweep.MiterType  = 2
    # sweep.SweepTolerance = scriptcontext.doc.ModelAbsoluteTolerance
    # sweep.SetToRoadlikeTop()
    # breps = sweep.PerformSweep(rail_crv, cross_sections)
    # for brep in breps: scriptcontext.doc.Objects.AddBrep(brep)
    # scriptcontext.doc.Views.Redraw()
    # 
    # # surface_id = rs.LastCreatedObjects()

    
    # METHOD1
    surface_id = rs.AddSweep1( crv, profile, True )    

    rs.CapPlanarHoles(surface_id)
    
    pt = rs.CurveAreaCentroid(profile)[0]
    pt2 = (pt.X, pt.Y, pt.Z+1)
    
    rev = rs.AddRevSrf( profile, (pt, pt2) )
    
    
    
    rs.MoveObject(surface_id, (0,0,z_pos))
    rs.MoveObject(rev, (0,0,z_pos))
    
    
    
    return [surface_id, rev]        

    
    
    rs.UnselectAllObjects()
    rs.SelectObjects([crv, profile])
    
    result = rs.Command("_-Sweep1 _Enter Style=RoadlikeTop _Enter", False)
            
    if result: 
        rs.DeleteObject(profile)
        surface_id = rs.LastCreatedObjects()

        rs.CapPlanarHoles(surface_id)
    
        rs.DeleteObjects(objs)
        rs.MoveObject(surface_id, (0,0,z_pos))

        return surface_id        
Esempio n. 10
0
def make_fingers(positive, negative, subdivisions):
  """
  intersect two collections of planes
  subdivide the intersections
  assign each subdivision to a guid from which it will be subtracted
  """

  # this vector is used to indicate axis of the intersection.
  # it needs to be parallel to the intersection
  # (there are other ways of doing this!)
  p0 = rs.GetPoint("select start of intersection")
  p1 = rs.GetPoint("select end of intersection")

  edge = rs.AddLine(p0, p1)
  vector = rs.VectorCreate(p0, p1)

  rs.EnableRedraw(False)

  # this dict maps a pair of planes (ps, ns) to their booleanintersection
  intersections = {}

  for ps in positive:
    for ns in negative:
      intersection = rs.BooleanIntersection(ps, ns, False)
      if intersection is not None:
        intersections[(ps, ns)] = intersection

  # here we construct some very large cylinders aligned with the axis you drew
  origins = []
  cylinders = []
  for i in range(subdivisions+1):
    origin = rs.EvaluateCurve(edge, rs.CurveParameter(edge, i * 1.0/(subdivisions)))
    origins.append(origin)

  rs.DeleteObject(edge)

  for i in range(subdivisions):
    plane = rs.PlaneFromNormal(origins[i], vector)
    circle = rs.AddCircle(plane, 100)
    planar_circle = rs.AddPlanarSrf(circle)

    extrusion_curve = rs.AddLine(origins[i], origins[i+1])
    cylinders.append(rs.ExtrudeSurface(planar_circle, extrusion_curve))

    rs.DeleteObject(circle)
    rs.DeleteObject(planar_circle)
    rs.DeleteObject(extrusion_curve)


  # we perform a boolean intersection between each intersection and
  # the cylinders to construct the fingers
  for key, intersection in intersections.items():
    ps, ns = key

    for i, cylinder in enumerate(cylinders):
      print "intersection", intersection
      print "cylinder", cylinder
      objs = [brep for brep in rs.BooleanIntersection(intersection, cylinder, False) if rs.IsBrep(brep)]
      # assign the resulting fingers to either the positive or negative
      if i % 2 == 0:
        guid_to_difference[ps].extend(objs)
      else:
        guid_to_difference[ns].extend(objs)

  DeleteItemOrList(cylinders)
  DeleteItemOrList(intersections.values())

  rs.EnableRedraw(True)
Esempio n. 11
0
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
Esempio n. 12
0
def automatic_constraints(mesh, surface_constraint, curve_constraints = [], point_constraints = []):
    """Defines the constraints on the vertices of the mesh on a point, a curve or a surface.
    
    Parameters
    ----------
    mesh : Mesh
        A mesh.
    surface_constraint : Rhino surface guid
        A surface to project vertices.
    curve_constraints : Rhino curve guids
        Curve features on surface to constrain vertices.
    point_constraints : Rhino point guids
        Point features on surface to constrain vertices.

    Returns
    -------
    constraints: dict
        Dictionary of constraints {vertex_key: (constraint_type, constraint_information)}.

    Raises
    ------
    -

    """

    constraints = {}

    surface_boundaries = surface_borders(surface_constraint, border_type = 0)

    # set point constraints at point feature, curve feature extremities and boundary curve corners
    constrained_points = []
    for curve_guid in surface_boundaries:
        start_tgt = rs.CurveTangent(curve_guid, rs.CurveParameter(curve_guid, 0))
        end_tgt = rs.CurveTangent(curve_guid, rs.CurveParameter(curve_guid, 1))
        # add only if not closed or closed with a kink
        if not rs.IsCurveClosed(curve_guid) or not rs.IsVectorParallelTo(start_tgt, end_tgt):
            start = geometric_key(rs.CurveStartPoint(curve_guid))
            end = geometric_key(rs.CurveEndPoint(curve_guid))
            if start not in constrained_points:
                constrained_points.append(start)
            if end not in constrained_points:
                constrained_points.append(end)

    for vkey in mesh.vertices():
        xyz = mesh.vertex_coordinates(vkey)
        geom_key = geometric_key(xyz)
        if geom_key in constrained_points:
            constraints[vkey] = ['point', xyz]

    # set boundary curve constraints
    split_vertices = [vkey for vkey, constraint in constraints.items() if constraint[0] == 'point']
    split_mesh_boundaries = mesh_boundaries(mesh, vertex_splits = split_vertices)

    # constrain a mesh boundary to a surface boundary if the two extremities of the mesh boundary are on the surface boundary
    for mesh_bdry in split_mesh_boundaries:

        if mesh_bdry[0] == mesh_bdry[-1]:
            crv_cstr = None
            for vkey in mesh_bdry:
                xyz = mesh.vertex_coordinates(vkey)
                for srf_bdry in surface_boundaries:
                    if is_point_on_curve(srf_bdry, xyz):
                        crv_cstr = srf_bdry
                        break
                if crv_cstr is not None:
                    break
            if crv_cstr is not None:
                for vkey in mesh_bdry:
                    xyz = mesh.vertex_coordinates(vkey)
                    if is_point_on_curve(crv_cstr, xyz) and vkey not in constraints:
                        constraints[vkey] = ['curve', crv_cstr]
                for i, vkey in enumerate(mesh_bdry):
                    if vkey not in constraints:
                        # find next contrained point
                        n_plus = 1
                        norm_t_plus = None
                        count = len(mesh_bdry)
                        while count > 0:
                            count -= 1
                            vkey_plus = mesh_bdry[i + n_plus - len(mesh_bdry)]
                            if vkey_plus in constraints:
                                norm_t_plus = rs.CurveNormalizedParameter(crv_cstr, rs.CurveClosestPoint(crv_cstr, mesh.vertex_coordinates(vkey_plus)))
                            else:
                                n_plus += 1
                        # find previous contrained point
                        n_minus = 1
                        norm_t_minus = None
                        count = len(mesh_bdry)
                        while count > 0:
                            count -= 1
                            vkey_minus = mesh_bdry[i - n_minus]
                            if vkey_minus in constraints:
                                norm_t_minus = rs.CurveNormalizedParameter(crv_cstr, rs.CurveClosestPoint(crv_cstr, mesh.vertex_coordinates(vkey_minus)))
                            else:
                                n_minus += 1
                        # calculate barycentric parameter and move to it
                        # dichotomy required in case of curve seam being between the two parameters
                        #print n_minus, norm_t_minus, n_plus, norm_t_plus
                        if norm_t_minus == norm_t_plus:
                            norm_t = (norm_t_plus + .5) % 1
                        elif norm_t_minus < norm_t_plus:
                            norm_t = (n_minus * norm_t_plus + n_plus * norm_t_minus) / (n_minus + n_plus)
                        else:
                            norm_t_plus += 1
                            norm_t = (n_minus * norm_t_plus + n_plus * norm_t_minus) / (n_minus + n_plus)
                        # update coordiantes
                        t = rs.CurveParameter(crv_cstr, norm_t)
                        x, y, z = rs.EvaluateCurve(crv_cstr, t)
                        attr = mesh.vertex[vkey]
                        attr['x'] = x
                        attr['y'] = y
                        attr['z'] = z
                        # store constraint
                        constraints[vkey] = ['curve', crv_cstr]
        
        else:
            for srf_bdry in surface_boundaries:
                start_xyz = mesh.vertex_coordinates(mesh_bdry[0])
                end_xyz = mesh.vertex_coordinates(mesh_bdry[-1])
                # if the mesh boundary extremities match the ones of the curve boundary...
                if is_point_on_curve(srf_bdry, start_xyz) and is_point_on_curve(srf_bdry, end_xyz):
                    # ... and if there is an intermediary mesh boundary vertex on this curve boundary (needed for two-sided boundary elements)
                    to_constrain = False
                    for vkey in mesh_bdry[1 : -1]:
                        if is_point_on_curve(srf_bdry, mesh.vertex_coordinates(vkey)):
                            to_constrain = True
                    if to_constrain:
                        crv_cstr = srf_bdry
                        for vkey in mesh_bdry:
                            xyz = mesh.vertex_coordinates(vkey)
                            if is_point_on_curve(crv_cstr, xyz) and vkey not in constraints:
                                constraints[vkey] = ['curve', crv_cstr]
                        for i, vkey in enumerate(mesh_bdry):
                            if vkey not in constraints:
                                # find next contrained point
                                n_plus = 1
                                norm_t_plus = None
                                count = len(mesh_bdry)
                                while count > 0:
                                    count -= 1
                                    vkey_plus = mesh_bdry[i + n_plus - len(mesh_bdry)]
                                    if vkey_plus in constraints:
                                        norm_t_plus = rs.CurveNormalizedParameter(crv_cstr, rs.CurveClosestPoint(crv_cstr, mesh.vertex_coordinates(vkey_plus)))
                                    else:
                                        n_plus += 1
                                # find previous contrained point
                                n_minus = 1
                                norm_t_minus = None
                                count = len(mesh_bdry)
                                while count > 0:
                                    count -= 1
                                    vkey_minus = mesh_bdry[i - n_minus]
                                    if vkey_minus in constraints:
                                        norm_t_minus = rs.CurveNormalizedParameter(crv_cstr, rs.CurveClosestPoint(crv_cstr, mesh.vertex_coordinates(vkey_minus)))
                                    else:
                                        n_minus += 1
                                # calculate barycentric parameter and move to it
                                # dichotomy required in case of curve seam being between the two parameters
                                #print n_minus, norm_t_minus, n_plus, norm_t_plus
                                if norm_t_minus == norm_t_plus:
                                    norm_t = (norm_t_plus + .5) % 1
                                elif norm_t_minus < norm_t_plus:
                                    norm_t = (n_minus * norm_t_plus + n_plus * norm_t_minus) / (n_minus + n_plus)
                                else:
                                    norm_t_plus += 1
                                    norm_t = (n_minus * norm_t_plus + n_plus * norm_t_minus) / (n_minus + n_plus)
                                # update coordiantes
                                t = rs.CurveParameter(crv_cstr, norm_t)
                                x, y, z = rs.EvaluateCurve(crv_cstr, t)
                                attr = mesh.vertex[vkey]
                                attr['x'] = x
                                attr['y'] = y
                                attr['z'] = z
                                # store constraint
                                constraints[vkey] = ['curve', crv_cstr]

    # constrain to point features
    point_constraints_keys = [geometric_key(rs.PointCoordinates(pt)) for pt in point_constraints]
    for vkey in mesh.vertices():
        xyz = mesh.vertex_coordinates(vkey)
        geom_key = geometric_key(xyz)
        if geom_key in point_constraints_keys:
            constraints[vkey] = ['point', xyz]

    # constrain to curve features
    for crv in curve_constraints:
        # extremities
        start = rs.CurveStartPoint(crv)
        start_geom_key = geometric_key(start)
        end = rs.CurveEndPoint(crv)
        end_geom_key = geometric_key(end)
        for vkey in mesh.vertices():
            xyz = mesh.vertex_coordinates(vkey)
            geom_key = geometric_key(xyz)
            if geom_key == start_geom_key:
                constraints[vkey] = ['point', xyz]
                start_key = vkey
            if geom_key == end_geom_key:
                constraints[vkey] = ['point', xyz]
                end_key = vkey
        # regular nodes
        path = [start_key]
        for nbr in mesh.vertex_neighbors(start_key):
            completed = False
            if mesh.is_vertex_on_boundary(nbr):
                continue
            path.append(nbr)
            count = len(list(mesh.vertices()))
            while count > 0:
                count -= 1
                u, v = path[-2], path[-1]
                fkey = mesh.halfedge[u][v]
                x = mesh.face_vertex_descendant(fkey, v)
                fkey = mesh.halfedge[x][v]
                w = mesh.face_vertex_descendant(fkey, v)
                path.append(w)
                if w == end_key:
                    completed = True
                    break
                elif mesh.is_vertex_on_boundary(w) or len(mesh.vertex_neighbors(w)) != 4:
                    break
            if completed:
                break
            else:
                path = [start_key]
        for vkey in path[1 : -1]:
            constraints[vkey] = ['curve', crv]

        
    # set surface constraints by default for the others
    for vkey in mesh.vertices():
        if vkey not in constraints:
            constraints[vkey] = ['surface', surface_constraint]

    # udpdate drawn mesh
    layer = 'pattern_topology'
    mesh_guid = rs.ObjectsByLayer(layer)[0]
    rs.DeleteObject(mesh_guid)
    mesh_guid = draw_mesh(mesh)
    rs.ObjectLayer(mesh_guid, layer)

    return constraints, surface_boundaries
Esempio n. 13
0
def stairHeight(route, width=48, height=120):
    """
    Makes a stair to specified height.

    input: route(pline), width (num), height(num)
    returns: Geo
    """
    try:
        rs.EnableRedraw(False)
        rs.SimplifyCurve(route)

        if route is None:
            print("ERROR: No path selected")
            return

        if (rs.UnitSystem() == 2):  #if mm
            maxRiserHeight = 180
            thickness = 200
        if (rs.UnitSystem() == 4):  #if m
            maxRiserHeight = .180
            thickness = .200
        if (rs.UnitSystem() == 8):  #if in"
            maxRiserHeight = 7
            thickness = 9

        negativeBoo = False
        if (height < 0):
            #if the stair
            negativeBoo = True
        landingEdges = []
        landings = []
        segments = rs.ExplodeCurves(route)
        if len(segments) < 1:
            segments = [rs.CopyObject(route)]
        landingHeight = []
        geometry = []

        #Check that all segments are lines
        for i in range(0, len(segments)):
            if not (rs.IsLine(segments[i])):
                print(
                    "ERROR: This function only accepts lines. No arcs or nurb curves."
                )
                rs.DeleteObjects(segments)
                return

        #first landing edge
        norm = rs.VectorRotate(rs.CurveTangent(segments[0], 0), 90, [0, 0, 1])
        norm = rs.VectorScale(rs.VectorUnitize(norm), width / 2)
        side1Pt = rs.VectorAdd(rs.CurveStartPoint(segments[0]), norm)
        side2Pt = rs.VectorAdd(rs.CurveStartPoint(segments[0]), -norm)
        landingEdges.append(rs.AddLine(side1Pt, side2Pt))

        #middle landing edges
        for i in range(0, len(segments) - 1):
            edgeList, landing = rampIntersection(segments[i], segments[i + 1],
                                                 width)
            landingEdges.append(edgeList[0])
            landingEdges.append(edgeList[1])
            landings.append(landing)

        #last landing edge
        norm = rs.VectorRotate(
            rs.CurveTangent(segments[-1], rs.CurveParameter(segments[-1], 1)),
            90, [0, 0, 1])
        norm = rs.VectorScale(rs.VectorUnitize(norm), width / 2)
        side1Pt = rs.VectorAdd(rs.CurveEndPoint(segments[-1]), norm)
        side2Pt = rs.VectorAdd(rs.CurveEndPoint(segments[-1]), -norm)
        landingEdges.append(rs.AddLine(side1Pt, side2Pt))

        #Add risers
        riserCrvs = []
        treadVecs = []
        numRisersPerRun = []
        numRisers = abs(int(math.ceil(height / maxRiserHeight)))
        risersSoFar = 0
        totalRun = getTotalRun(landingEdges)
        optTreadDepth = totalRun / (numRisers - 1)
        #2R+T = 635
        riserHeight = height / numRisers
        if (negativeBoo):
            curRiserHeight = 0
        else:
            curRiserHeight = riserHeight
        for i in range(0, len(landingEdges), 2):  #find numRisers in each run
            a = rs.CurveMidPoint(landingEdges[i])
            b = rs.CurveMidPoint(landingEdges[i + 1])
            runDist = rs.Distance(a, b)
            numRisersThisRun = int(round((runDist / optTreadDepth), 0))
            if (numRisersThisRun == 0):
                numRisersThisRun = 1
            if (i == len(landingEdges) -
                    2):  #if last run, add the rest of the risers
                numRisersThisRun = numRisers - risersSoFar
            else:
                risersSoFar = risersSoFar + numRisersThisRun
            numRisersPerRun.append(numRisersThisRun)

        #Create Risers on Plan
        for i in range(0, len(landingEdges), 2):
            run = []
            a = rs.CurveMidPoint(landingEdges[i])
            b = rs.CurveMidPoint(landingEdges[i + 1])
            centerStringer = rs.AddLine(a, b)
            runDist = rs.Distance(a, b)
            numRisersThisRun = numRisersPerRun[int(i / 2)]  #risers in this run
            tarPts = rs.DivideCurve(centerStringer,
                                    numRisersThisRun,
                                    create_points=False)
            rs.DeleteObject(centerStringer)
            for j in range(0, numRisersThisRun + 1):
                if (j == 0):
                    treadVecs.append(rs.VectorCreate(tarPts[0], tarPts[1]))
                transVec = rs.VectorCreate(tarPts[0], tarPts[j])
                run.append(rs.CopyObject(landingEdges[i], -transVec))
            riserCrvs.append(run)
            print('Flight {0} has {1} risers: {3}" tall, Treads: {2}" deep'.
                  format(
                      int(i / 2) + 1, numRisersThisRun,
                      rs.VectorLength(treadVecs[int(i / 2)]), riserHeight))
        #Move riser edges vertically
        for i in range(0, len(riserCrvs)):
            triangles = []
            if (negativeBoo):
                for j in range(0, len(riserCrvs[i]) - 1):
                    #if stairs descending
                    rs.MoveObject(
                        riserCrvs[i][j],
                        rs.VectorAdd([0, 0, curRiserHeight], -treadVecs[i]))
                    riserGeo = rs.ExtrudeCurveStraight(riserCrvs[i][j],
                                                       [0, 0, 0],
                                                       [0, 0, riserHeight])
                    treadGeo = rs.ExtrudeCurveStraight(riserCrvs[i][j],
                                                       [0, 0, 0], treadVecs[i])
                    stPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][j]))
                    pt1 = rs.CopyObject(
                        stPt, [0, 0, riserHeight])  #first riser in run
                    pt2 = rs.CopyObject(stPt, treadVecs[i])  #last riser in run
                    triCrv = rs.AddPolyline([stPt, pt1, pt2, stPt])
                    triangles.append(rs.AddPlanarSrf(triCrv))
                    geometry.append(riserGeo)  #riser
                    geometry.append(treadGeo)  #tread
                    curRiserHeight = curRiserHeight + riserHeight
                    rs.MoveObject(riserCrvs[i][j], treadVecs[i])
                    #cleanup
                    rs.DeleteObject(triCrv)
                    rs.DeleteObject(stPt)
                    rs.DeleteObject(pt1)
                    rs.DeleteObject(pt2)
            else:
                for j in range(0, len(riserCrvs[i]) - 1):
                    #if stairs ascend
                    rs.MoveObject(riserCrvs[i][j], [0, 0, curRiserHeight])
                    stPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][j]))
                    pt1 = rs.CopyObject(
                        stPt, [0, 0, -riserHeight])  #first riser in run
                    pt2 = rs.CopyObject(stPt,
                                        -treadVecs[i])  #last riser in run
                    triCrv = rs.AddPolyline([stPt, pt1, pt2, stPt])
                    triangles.append(rs.AddPlanarSrf(triCrv))
                    riserGeo = rs.ExtrudeCurveStraight(riserCrvs[i][j],
                                                       [0, 0, 0],
                                                       [0, 0, -riserHeight])
                    treadGeo = rs.ExtrudeCurveStraight(riserCrvs[i][j],
                                                       [0, 0, 0],
                                                       -treadVecs[i])
                    geometry.append(riserGeo)  #riser
                    geometry.append(treadGeo)  #tread
                    curRiserHeight = curRiserHeight + riserHeight
                    #cleanup
                    rs.DeleteObject(triCrv)
                    rs.DeleteObject(stPt)
                    rs.DeleteObject(pt1)
                    rs.DeleteObject(pt2)

            #Make Stringer
            if (negativeBoo):
                firstStartPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][0]))
                lastStartPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][-2]))
                #rs.MoveObject(firstStartPt, [0,0,riserHeight]) #first riser in run
                rs.MoveObject(lastStartPt, -treadVecs[i])  #last riser in run
                rs.MoveObject(lastStartPt,
                              [0, 0, riserHeight])  #last riser in run
            else:
                firstStartPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][0]))
                lastStartPt = rs.AddPoint(rs.CurveStartPoint(riserCrvs[i][-2]))
                rs.MoveObject(firstStartPt,
                              [0, 0, -riserHeight])  #first riser in run
                rs.MoveObject(lastStartPt, -treadVecs[i])  #last riser in run
            stringerCrv = rs.AddLine(firstStartPt, lastStartPt)
            stringerSrf = rs.ExtrudeCurveStraight(stringerCrv, [0, 0, 0],
                                                  [0, 0, -thickness])
            triangles.append(stringerSrf)
            stringer = makeFace(triangles)
            stringerVec = rs.VectorCreate(rs.CurveEndPoint(riserCrvs[i][0]),
                                          rs.CurveStartPoint(riserCrvs[i][0]))
            underside = rs.ExtrudeCurveStraight(
                stringerCrv, rs.CurveStartPoint(riserCrvs[i][0]),
                rs.CurveEndPoint(riserCrvs[i][0]))
            geometry.append(rs.MoveObject(underside, [0, 0, -thickness]))
            geometry.append(rs.CopyObject(stringer, stringerVec))
            geometry.append(stringer)

            #cleanup
            rs.DeleteObject(firstStartPt)
            rs.DeleteObject(lastStartPt)
            rs.DeleteObject(stringerCrv)
            rs.DeleteObject(stringerSrf)

        #Move Landings
        lastLandingHeight = 0
        for i in range(0, len(segments) - 1):
            landingHeight = lastLandingHeight + numRisersPerRun[i] * riserHeight
            rs.MoveObject(landings[i], [0, 0, landingHeight])
            landingTopSrf = rs.AddPlanarSrf(landings[i])
            landingBtmSrf = rs.CopyObject(landingTopSrf, [0, 0, -thickness])
            geometry.append(landingTopSrf)
            geometry.append(landingBtmSrf)
            lastLandingHeight = landingHeight
            landingEdgesToEx = rs.ExplodeCurves(landings[i])
            geometry.append(
                rs.ExtrudeCurveStraight(landingEdgesToEx[1], [0, 0, 0],
                                        [0, 0, -thickness]))
            geometry.append(
                rs.ExtrudeCurveStraight(landingEdgesToEx[2], [0, 0, 0],
                                        [0, 0, -thickness]))
            rs.DeleteObjects(landingEdgesToEx)

        #Create final geometry
        joinedGeo = rs.JoinSurfaces(geometry, True)
        holes = rs.DuplicateSurfaceBorder(joinedGeo)
        cap = rs.AddPlanarSrf(holes)
        newGeo = rs.ExplodePolysurfaces(joinedGeo, True)
        for i in cap:
            newGeo.append(i)
        FinalGeo = rs.JoinSurfaces(newGeo, True)

        #cleanup
        try:
            rs.DeleteObjects(segments)
        except:
            rs.DeleteObject(segments)
        rs.DeleteObjects(holes)
        rs.DeleteObjects(landings)
        rs.DeleteObjects(landingEdges)
        for i in riserCrvs:
            rs.DeleteObjects(i)

        rs.EnableRedraw(True)
        return FinalGeo
    except:
        print "Error"
        return None