Loose offset Curve script

Attached is a work in progress of a loose offset for curves. It basically offsets the control points in the direction normal to the closest point on the curve. The resulting curve will have the same control point count and degree as the input, as well as the same starting point, which makes it ideal for making nice lofts.

    """
    This script does what the name says: it allows you to offset a curve loosely.
    This means the offset curve has the same structure and degree, and same amount
    of control points as the original curve.
    Option to offset both sides.

    ***********************************
    * script written by Gijs de Zwart *
    * www.studiogijs.nl               *
    * March, 2016                     *
    ***********************************

    """


    import rhinoscriptsyntax as rs
    import Rhino
    import scriptcontext as sc

    def OffsetCrvLoose():

        crv=rs.GetObject("select curve to offset loosely",rs.filter.curve, True)
        if crv==None:
            return
        if not rs.IsCurvePlanar(crv):
            print "Sorry, but that curve is not planar."
            return
        if rs.IsPolyCurve(crv):
            print "This simple script works only for single open or closed curves"
            return
        offset=rs.GetReal("offset amount",5)

        if offset==None or offset==0:
            return
        both_sides=rs.GetBoolean("Offset both sides?",["both_sides","off","on"], False)[0]
        bPeriodic=False
        #rs.EnableRedraw(False)
        pts=rs.CurvePoints(crv)
        degree=rs.CurveDegree(crv)
        if rs.IsCurvePeriodic(crv):
            pts=rs.CullDuplicatePoints(pts,0.01)
            bPeriodic=True
        offset_pts=[]
        offset_pts2=[]#if both_sides=true
        plane=rs.CurvePlane(crv)
        axis=plane.ZAxis
        for pt in pts:
            cp=rs.CurveClosestPoint(crv,pt)
            v=rs.CurveTangent(crv,cp)
            v=rs.VectorUnitize(v)
            v*=offset
            v=rs.VectorRotate(v,90,axis)
            pt_=rs.AddPoint(pt)
            #create points for offset on one side of the curve
            movedpt=rs.MoveObject(pt_,v)
            newpt=rs.coerce3dpoint(movedpt)
            offset_pts.append(newpt)
            #create points for offset on other side of the curve
            movedpt=rs.MoveObject(pt_,-2*v)
            newpt=rs.coerce3dpoint(movedpt)
            offset_pts2.append(newpt)
            rs.DeleteObject(pt_)
        nc = Rhino.Geometry.NurbsCurve.Create(bPeriodic,degree,offset_pts)
        nc2 = Rhino.Geometry.NurbsCurve.Create(bPeriodic,degree,offset_pts2)

        if not both_sides:
            if nc.GetLength(0.1)>nc2.GetLength(0.1):#get the longest curve...
                if offset>0:#...and add it to the document for positive offsets...
                    sc.doc.Objects.AddCurve(nc)
                else:#...or the shortest for negative offsets.
                    sc.doc.Objects.AddCurve(nc2)
            else:
                if offset>0:
                    sc.doc.Objects.AddCurve(nc2)
                else:
                    sc.doc.Objects.AddCurve(nc)
        else:#add both curves to the document
            sc.doc.Objects.AddCurve(nc)
            sc.doc.Objects.AddCurve(nc2)

        rs.EnableRedraw(True)
        sc.doc.Views.Redraw()
    if __name__ == '__main__':
        OffsetCrvLoose()
3 Likes