Curve On Surface with rebuild (how to solve preview interruption?)[solved]

as an exercise, I’ve made a curve on surface script that rebuilds the curve while drawing, and gives the user the option to change number of points during the command.
I’ve taken the approach to add an empty point to the picked points that gets overriden by the dynamicdraw function CurrentPoint. Everything seems to work except I can’t solve the issue that stops the dynamic draw from drawing the curve as soon as the CurrentPoint is equal to the last picked point.
I was able to solve the issue for the ‘baked curve’ by testing the last point to the previous point and remove it, but for the dynamic draw routine, I can’t figure out how to do that. Any pointers?

the script:

import Rhino
import rhinoscriptsyntax as rs
from System.Drawing import Color
import scriptcontext as sc

def DynamicCrvOnSrf():
    
    def drawMyCurve(sender,e):
        points[-1]=e.CurrentPoint#change last point to CurrentPoint
        curve=rhsrf.InterpolatedCurveOnSurface(points,0.01)
        nc = curve.ToNurbsCurve()
        ptCount=optInt.CurrentValue
        nc=nc.Rebuild(ptCount,3, True)
        ncpoints = [nc.Points[i].Location for i in xrange(nc.Points.Count)]
        e.Display.DrawCurve(nc, Color.LightCyan,2)
        e.Display.DrawPoints(ncpoints,Rhino.Display.PointStyle.Simple,5,Color.Cyan)
        e.Display.DrawPoints(points,Rhino.Display.PointStyle.X,1,Color.Blue)

    def getPoint():
        while True:
            result = gp.Get()
            if result == Rhino.Input.GetResult.Point:
                gp.SetCommandPrompt("Next point")
                pt=gp.Point()
                newpoint=rs.AddPoint(pt)
                snapPoints.append(newpoint)
                #append first picked point
                if points==[]:
                    points.append(pt)
                #check if next picked point is same as previous
                if len(points)>1:
                    a=round(points[-1].X,2)
                    b=round(points[-2].X,2)
                    if a==b:
                        del points[-1]
                #add empty point to list
                #will get assigned in drawMyCurve()
                points.append(Rhino.Geometry.Point3d)
                gp.DynamicDraw+=drawMyCurve
                #recursion: getpoint calling itself if a point has been picked:
                getPoint()
            elif result == Rhino.Input.GetResult.Option:
                #go back to point selection mode
                getPoint()
            elif result == Rhino.Input.GetResult.Undo:
                del points[-2]
                rs.DeleteObject(snapPoints[-1])
                getPoint()
            #pressing spacebar, enter
            elif result == Rhino.Input.GetResult.Nothing and len(points)>2:#2 picked points +1 temporary point
                #remove last added preview point
                del points[-1]
                ptCount=optInt.CurrentValue
                rs.DeleteObjects(snapPoints)
                newcrv=rs.AddInterpCrvOnSrf(srf, points)
                rs.RebuildCurve(newcrv,3,ptCount)    
                sc.doc.Views.Redraw()
            #pressing esc
            else:
                rs.DeleteObjects(snapPoints)
            break

    srf=rs.GetObject("Select surface to draw curve on", rs.filter.surface)
    if srf==None:
        return
    rhsrf=rs.coercesurface(srf)
    gp=Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt("Start of Curve")
    gp.Constrain(rhsrf, False)
    gp.AcceptNothing(True)
    gp.AcceptUndo(True)
    Rhino.ApplicationSettings.SmartTrackSettings.UseSmartTrack=False
    points=[]
    snapPoints=[]
    optInt=Rhino.Input.Custom.OptionInteger(20,4,100)
    gp.AddOptionInteger("ptCount",optInt)
    getPoint()

if( __name__ == "__main__" ):
    DynamicCrvOnSrf()

@Gijs,

Not sure why redraw stops when the mouse is still over the last picked point, but for some strange reason Rhino or IronPython often does not properly print out exeptions which happened in a delegate. I therefore suggest to put the complete content in the drawMyCurve(sender, e) function in a try / except block like this:

def drawMyCurve(sender, e):
    try:
        points[-1] = e.CurrentPoint
        curve = rhsrf.InterpolatedCurveOnSurface(points,0.01)
        e.Display.DrawCurve(curve, Color.LightCyan,2)
    except Exception as ex: 
        template = "An exception of type {0} occured. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print message
        return 

so you can see that sometimes, the interpolated curve is not created. I guess this might be the case when the e.CurrentPoint has been added to as last point and the same location is still active.

I´ve also been able to reproduce a crash with this script which you reported in this thread. After i´ve removed this line from the loop:

gp.DynamicDraw+=drawMyCurve

and put it below the line where you add the option integer just above the first getPoint(), i´ve not been able to crash it. I guess if the delegate gets added with every loop, it might have caused an overflow.

c.

Thanks @clement, I will try out your suggestion. I think the curve is not shown because of the duplicate point, but I have no clue how to prevent this from happening. It should be something like:
If e.CurrentPoint is equal to previous point, temporarily remove it from the point list that is used to create the curve. Is that possible?

thanks again @clement,

using the try/except is helpful for debugging the dynamic draw! Indeed otherwise Rhino will not report back any errors.
I managed to solve the interrupted preview as well, see code below. But somehow it still feels a bit clunky how the code is built up, even though it now does exactly all the things I wanted it to do.
Btw instead of adding the dynamicdraw before the first get point operation, I moved it to the place right after the first point has been added, because in the first line I am assigning e.CurrentPoint to the last point in the points list and would otherwise be out of range.

no more crashes now on the mac :slight_smile:

"""
This script allows you draw a controlpoint curve on a surface, where you can
dynamically change the amount of points and see a preview of the resulting
curve while drawing.

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

"""

import Rhino
import rhinoscriptsyntax as rs
from System.Drawing import Color
import scriptcontext as sc

def DynamicCrvOnSrf():

    def drawMyCurve(sender,e):

        points[-1]=e.CurrentPoint#change last point to CurrentPoint
        pts = [points[i] for i in xrange(len(points))]
        curve=rhsrf.InterpolatedCurveOnSurface(points,0.01)
        if curve==None:
            del pts[-1]
            curve=rhsrf.InterpolatedCurveOnSurface(pts,0.01)
        if curve:
            nc = curve.ToNurbsCurve()
            ptCount=optInt.CurrentValue
            nc=nc.Rebuild(ptCount,3, True)
            ncpoints = [nc.Points[i].Location for i in xrange(nc.Points.Count)]
            e.Display.DrawCurve(nc, Color.LightCyan,2)
            e.Display.DrawPoints(ncpoints,Rhino.Display.PointStyle.Simple,5,Color.Cyan)
            e.Display.DrawPoints(points,Rhino.Display.PointStyle.X,1,Color.Blue)

    def getPoint():
        if points!=[] and len(points)==4:
            gp.AddOption("Close")
        while True:
            result = gp.Get()
            if result == Rhino.Input.GetResult.Point:
                gp.AcceptUndo(True)
                gp.SetCommandPrompt("Next point")
                pt=gp.Point()
                newpoint=rs.AddPoint(pt)
                snapPoints.append(newpoint)
                #append first picked point
                if points==[]:
                    points.append(pt)
                    gp.DynamicDraw+=drawMyCurve
                #check if next picked point is same as previous
                if len(points)>1:
                    a=round(points[-1].X,2)
                    b=round(points[-2].X,2)
                    if a==b:
                        del points[-1]
                #add empty point to list
                #will get assigned in drawMyCurve()
                points.append(Rhino.Geometry.Point3d)

                #recursion: getpoint calling itself if a point has been picked:
                getPoint()
            elif result == Rhino.Input.GetResult.Option:
                #go back to point selection mode
                if gp.OptionIndex()==1:
                    getPoint()
                elif gp.OptionIndex()==2:
                    #close the curve
                    del points[-1]
                    ptCount=optInt.CurrentValue
                    pt=points[0]
                    a=round(points[-1].X,2)
                    b=round(points[0].X,2)
                    if a==b:
                        del points[-1]
                    points.append(pt)
                    rs.DeleteObjects(snapPoints)
                    newcrv=rs.AddInterpCrvOnSrf(srf, points)
                    rs.RebuildCurve(newcrv,3,ptCount)
                    sc.doc.Views.Redraw()

            elif result == Rhino.Input.GetResult.Undo:
                if len(points)>1:
                    del points[-2]
                    rs.DeleteObject(snapPoints[-1])
                    del snapPoints[-1]
                if len(points)<=1:
                    gp.AcceptUndo(False)
                getPoint()
            #pressing spacebar, enter
            elif result == Rhino.Input.GetResult.Nothing and len(points)>2:#2 picked points +1 temporary point
                #remove last added preview point
                del points[-1]
                ptCount=optInt.CurrentValue
                rs.DeleteObjects(snapPoints)
                newcrv=rs.AddInterpCrvOnSrf(srf, points)
                rs.RebuildCurve(newcrv,3,ptCount)
                sc.doc.Views.Redraw()
            #pressing esc
            else:
                rs.DeleteObjects(snapPoints)
            break

    srf=rs.GetObject("Select surface to draw curve on", rs.filter.surface)
    if srf==None:
        return
    rhsrf=rs.coercesurface(srf)
    gp=Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt("Start of Curve")
    gp.Constrain(rhsrf, False)
    gp.AcceptNothing(True)
    Rhino.ApplicationSettings.SmartTrackSettings.UseSmartTrack=False
    points=[]
    snapPoints=[]
    optInt=Rhino.Input.Custom.OptionInteger(20,4,100)
    gp.AddOptionInteger("ptCount",optInt)
    getPoint()

if( __name__ == "__main__" ):
    DynamicCrvOnSrf()


i hope this gets some attention to @stevebaer, just found i can write a complete nonsense to a delegate and rhino does not show any error :wink:

Agreed. I guess it would be cleaner if there is a way to use Rhino.Input.Custom.GetPoint with some kind of GetMultiple but this does not exist as far as i see. Even the RhinoScript method rs.GetPoints requires a loop of gp.Get(). It is located here[quote=“Gijs, post:4, topic:31313”]
no more crashes now on the mac btw.
[/quote]

Great :slight_smile: Only thing which is missing is that it stops drawing if the curve is closed.

c.

1 Like

what do you mean with:

…stops drawing if the curve is closed

when looking at the way Rhino does this, it will add the curve to the document and stop drawing, so in my script it works the same, or do you see other behavior?

btw I just noticed one thing that I need to fix and that is, when you manually close the curve and then click on “close” it will throw errors.

@Gijs, yes by using the Close option it works ok, but manually clicking the last point and snap to the first does not get me out of the loop yet.

c.

that’s true, but this means it works as designed :slight_smile:
the same is the case with Rhino’s built in interpolated curve on surface

close error now corrected (script above is modified)

1 Like

What would you like me to do? If I don’t catch the exception, then Rhino will crash. If I do catch the exception, I can let it silently go by or … ?

Open to suggestions

Hi Steve,

i think if an exception is fired it might be more useful to show it to the user instead of letting it silently go by. Are there any drawbacks by showing it which i am not aware of ?

c.

I understand that I should only show it once even though you could potentially be generating 100 exceptions per seconds as you move the mouse around the screen. I can show a geeky exception thrown message box, but it doesn’t seem like it would be incredibly useful to the end user (or maybe it is.)

A message box imho is not as good as printing it like other exceptions are printed in the output area of the python editor.

c.

I would also vote for just printing it like other exceptions. I don’t understand though why it is necessary to print the same error many times in a row instead of just once and stop the script just like try/except is doing. But better 100 exceptions then just silence. :slight_smile:

Hi Steve,

I think the current behaviour is the best default behaviour. Getting printed feedback, a dialog or even crashing is not what one wants to block functionality from a feedback process like dynamic drawing. I think it a valid case to ignore exceptions for the user.

However from the developer point of view it’s frustrating to have no feedback on the exceptions thrown.

Would it be feasable to be able to explicitely set a flag to print exceptions to the console.

It would be an easy way to switch between normal and debug mode…

My2c

  • Willem