Help: DynamicDraw project on mesh

Hi, I am exploring the DynamicDraw function, but apparently I lack the basic knowledge.

I would like to know how I can project temporary curves on a mesh. A simple example would be:
Pick a mesh and then have a mouse over funtion that projects a circle down on the point on the mesh that the cursor hoovers above.

Imagine cursor arrow in middle of yellow projected circle:


below script might be used for further refinement. I´m using a circle created in the XYPlane, then move the plane to the mouse cursor which is constrained on the pre-picked mesh. (1.3 KB)



That should get me going, thanks!

Very cool Clement. How to I (quickly) learn all this ? :wink:


1 Like

Hi @Jarek,

i´m still practising here as well :wink: The best source imho are the syntax files located here:

C:\Users\UserName\AppData\Roaming\McNeel\Rhinoceros\5.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings\lib\rhinoscript


1 Like

thanks Clement, this is a great tip, and plenty of info to look at.


Ok, this is painful but rewarding! :slight_smile:

I have now managed to modify your script so it dynamically projects a height curve for the given point, and I have two questions:

1: How do I send the entire “rc” list of curves to sender.Tag, (not only rc[0]) and how do I then add all those curves to the document?

2: How can I replace the cursor tool tip text? (Now it is “On mesh”) (1.6 KB)

Hi Jorgen,

I’ve edited the script to return all intersections.
While at it I simplified the script by using the mesh-plane intersection rather than projecting a line.
I do not know however how stable that is given the state of Rhino mesh tools.

# dynamic project height curve on mesh, 2016, Jorgen Holo
# Based on "dynamic project circle on mesh, 2016, Clement Greiner - CG3D"
# Edited by willemderks to use mesh-plane intersection and add all intersections

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

def SliceMesh():
    mesh_id = rs.GetObject("Select mesh", 32, True, False)
    if not mesh_id: return
    mesh = rs.coercemesh(mesh_id, True)

    gp = Rhino.Input.Custom.GetPoint()
    gp.Constrain(mesh, False)
    gp.Tag = None
    gp.SetCommandPrompt("Point on mesh")

    def GetPointDynamicDrawFunc(sender, args):
        #simplified by using mesh-plane intersection
        plane = Rhino.Geometry.Plane.WorldXY
        plane.Origin = args.CurrentPoint
        rc = Rhino.Geometry.Intersect.Intersection.MeshPlane(mesh,plane)
        if rc.Count != 0:
            for polyline in rc:
                args.Display.DrawPolyline(polyline, Color.Red, 3)
            #send all projected curves 'back'
            sender.Tag = rc

    gp.DynamicDraw += GetPointDynamicDrawFunc
    getres = gp.Get()
    if gp.CommandResult() != Rhino.Commands.Result.Success: 
        return None

    if gp.Tag:
        #use generater to create and return a list of new curves
        crv_ids = [scriptcontext.doc.Objects.AddPolyline(polyline) for polyline in gp.Tag]


if __name__=="__main__":

Nice move, I was going to test with mesh mesh intersection since I saw that a clippingplane moves through complex meshes so much faster, so you just saved me a lot of time! :smiley:

But do you know why the mesh-mesh intersection is so much faster than projecting a curve?

OT: Do you know why “prompt” causes lagging if I move the mouse too fast over the mesh?

Hi Jorgen,

A projected curve is more computational intensive because the smooth curve is projected on the mesh faces.
So the smoothness of the curve in maintained, this needs more calculations.

I don’t know why exactly but I do know that updating the commandline is time intensive. For each move of the mouse the commandline needs to be updated as well, causing lag when the mouse is moved faster than the prompt can be updated.


1 Like

you might try this in the GetPointDynamicDrawFunc and replace the prompt call:



Thanks, but I could not get that to work,
I had already tried Rhino.UI.MouseCursor.SetToolTip(str(zValue)) with no luck.

Neither throws an error, they just don’t work.

@dale do you think you can push me in the right direction?

thats strange, if i paste that line into willem’s script, i get the cursor preview:

but while moving the mouse i sometime still see the “onmesh” tooltip.


Hm, that’s odd, nothing happens here. I replaced the “prompt” line.

I’ll check on another machine later on, now off to put the kids to bed.

I did not get it to work either.


Ok, just found why it works over here but not on a default configured system:

i guess there is another way to draw the distance (z-height) but this will be 2d text instead of tooltip text…


1 Like

Well spotted,
I got it to work, but wow, that really brings down the draw speed on complex meshes, at least to half.

I’m off for a few days now, thanks a lot so far, really appreciate the help.
More later guys.

Yes, everything you do in a conduit slows things down. Therefore try to put as much of the code outside the GetPointDynamicDrawFunc. I doubt that drawing the 2d text in the conduit is much faster but you might try:

    plane = Rhino.Geometry.Plane.WorldXY
    wc = Rhino.DocObjects.CoordinateSystem.World
    sc = Rhino.DocObjects.CoordinateSystem.Screen

    def GetPointDynamicDrawFunc(sender, args):
        # simplified by using mesh-plane intersection
        plane.Origin = args.CurrentPoint
        rc = Rhino.Geometry.Intersect.Intersection.MeshPlane(mesh,plane)

        # draw text of height near mouse cursor
        vp = args.Viewport
        xform = vp.GetTransform(wc, sc)
        current_pt = args.CurrentPoint
        screen_pt = Rhino.Geometry.Point2d(current_pt.X, current_pt.Y-25)
        z_height = round(plane.Origin.Z, 3).ToString()
        if vp.Id == scriptcontext.doc.Views.ActiveView.ActiveViewportID:
            args.Display.Draw2dText(z_height, Color.White, screen_pt, True)

        if rc.Count != 0:
            for polyline in rc:
                args.Display.DrawPolyline(polyline, Color.Red, 3)
            #send all projected curves 'back'
            sender.Tag = rc

This draws the z-height 25px above the mouse cursor in the active viewport only, without having to change the Cursor ToolTips settings.

btw. note that Rhino does not show any errors in the python console if they happened in a delegate (the function which does the dynamic display). I´ve reported that in the past.


1 Like

Another option, to adding a custom drawing function, is to derive class from GetPoint. In doing this, it might be easier to separate your calculation code from your drawing code.

For example:

import Rhino

# Rhino.Input.Custom.GetPoint derived class 
class MyGetPoint(Rhino.Input.Custom.GetPoint):
    # Class initializer
    def __init__(self):
        # TODO: add class initialization code here
    # OnMouseMove override
    def OnMouseMove(self, e):
        # TODO: add calculation code here,
        # then call the base class so default calculations
        # can be made.
        Rhino.Input.Custom.GetPoint.OnMouseMove(self, e)
    # OnDynamicDraw override
    def OnDynamicDraw(self, e):
        # TODO: add drawing code here,
        # then call the base class so default drawing
        # can be performed.
        Rhino.Input.Custom.GetPoint.OnDynamicDraw(self, e)

gp = MyGetPoint();
gp.SetCommandPrompt('Pick a point')

A bit OT, but I never messed with the ToolTips options. I tried just enabling it and definitely could be a good for people that learn Rhino to have Prompt checked…
But two things I have noticed:

  1. When enabled with just Osnap, it show osnap a bit nicer than default osnap tooltip with disabled Tooltips (this one has a cast shadow and AA text over here)
  2. I can’t seem to make the Background color / text color active

Thanks for bringing this up !