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()
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:
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.
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?
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
"""
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
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 Only thing which is missing is that it 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.
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 ?
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.)
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.
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…