import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import math

def SetCurveLength():
    pi = math.pi
    def closed_curve_filter( rhino_object, geometry, component_index):
        if geometry.IsClosed:
            print "X"
            return False
        return True
        
    looped = False
    while True:
        length = 1
        if sc.sticky.has_key('CRV_EX_LENGTH'):
            length = sc.sticky['CRV_EX_LENGTH']
        go = Rhino.Input.Custom.GetObject()
        
        strMessage = "Pick the end of the curve to move."
        if looped: strMessage = "Pick the end of the curve to move. Press enter to finish."
        
        go.SetCommandPrompt(strMessage)
        go.GeometryFilter = Rhino.DocObjects.ObjectType.Curve
        go.SetCustomGeometryFilter(closed_curve_filter)
        
        opLen = Rhino.Input.Custom.OptionDouble(length)
        go.AddOptionDouble("Length", opLen)
        
        go.AcceptNumber(True, False)
        go.EnablePreSelect(False, True)
        
        rc = go.Get()
        
        if go.CommandResult() != Rhino.Commands.Result.Success:
            return go.CommandResult()
            
        if rc == Rhino.Input.GetResult.Option:
            length = opLen.CurrentValue
            sc.sticky['CRV_EX_LENGTH'] = length
            continue
            
        if rc == Rhino.Input.GetResult.Number:
            length = go.Number()
            sc.sticky['CRV_EX_LENGTH'] = length
            continue
            
        if rc == Rhino.Input.GetResult.Object:
            objref = go.Object(0)
            geo = objref.Geometry()
            crntLen = geo.GetLength()
            selPt = objref.SelectionPoint()
            
            p_rc, pickPar = geo.ClosestPoint(selPt)
            
            if geo.IsArc():
                if length >= 2*pi* geo.Radius:
                    newCrv = Rhino.Geometry.Circle(geo.Arc.Plane,geo.Arc.Radius)
                    sc.doc.Objects.Replace(objref, newCrv)
                    print "Desired arc length is longer than the full circle circumference. The circle was added. Length is " + str(round(newCrv.Circumference, 4))
                    looped = True
                    sc.doc.Views.Redraw()
                    continue
                    
                exStyle = Rhino.Geometry.CurveExtensionStyle.Arc
            else:
                exStyle = Rhino.Geometry.CurveExtensionStyle.Smooth 
            idx = 0
            if length > 0:
                
                crvEnd = Rhino.Geometry.CurveEnd.Start
                flip = False
                if pickPar>= geo.Domain.Mid:
                    crvEnd =Rhino.Geometry.CurveEnd.End
                    exLen = length-crntLen
                    if exLen > 0:
                        newCrv = geo.Extend(crvEnd, exLen,exStyle)
                    else:
                        if flip: geo.Reverse()
                        subPt = geo.PointAtLength(length)
                        if subPt:
                            parRc, subPar = geo.ClosestPoint(subPt)
                            split= geo.Split(subPar)
                            newCrv = split[0]
                            if flip: newCrv.Reverse()
                else:
                    crvEnd =Rhino.Geometry.CurveEnd.Start
                    exLen = length-crntLen
                    if exLen > 0:
                        newCrv = geo.Extend(crvEnd, exLen,exStyle)
                    else:
                        idx = 1
                        subPt = geo.PointAtLength(geo.GetLength()-length)
                        if subPt:
                            parRc, subPar = geo.ClosestPoint(subPt)
                            split= geo.Split(subPar)
                            newCrv = split[idx]
                            
            elif length < 0:
                exLen = abs(length)
                if pickPar>= geo.Domain.Mid:
                    splitPt = geo.PointAtStart
                    crvEnd =Rhino.Geometry.CurveEnd.Start
                    idx = 0
                else:
                    crvEnd =Rhino.Geometry.CurveEnd.End
                    splitPt = geo.PointAtEnd
                    idx = 1

                newCrv = geo.Extend(crvEnd, exLen,exStyle)
                parRc, subPar = newCrv.ClosestPoint(splitPt)
                split= newCrv.Split(subPar)
                newCrv = split[idx]
                newCrv.Reverse()
                
            sc.doc.Objects.Replace(objref, newCrv)
            print "Curve length set to " + str(round(length, 3))
            looped = True
            sc.doc.Views.Redraw()
            continue
    
if __name__ == '__main__':SetCurveLength()

