[Help] How to draw line segments while picking the points

Hi,

First a small complain, RhinoCommon, RhinoPython, and the other APIs are really not helpful for beginner programmers. Same goes for the examples. Very few comments not really explaining what is going on.

Now on the point. Can anyone explain, please…
How can I make the code below draw the line segments between the points while I’m creating them? I am getting its length after all.
Please excuse me for the useless lines, I’m trying to understand how it all works.

import Rhino
import scriptcontext

def zPolyline():
    # For this example we will use a GetPoint class, but all of the custom
    # "Get" classes support command line options.
    gp = Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt("GetPoint with options")

    # set up the options
    intOption = Rhino.Input.Custom.OptionInteger(1, 1, 99)
    dblOption = Rhino.Input.Custom.OptionDouble(2.2, 0, 99.9)
    boolOption = Rhino.Input.Custom.OptionToggle(True, "Off", "On")
    listValues = "Item0", "Item1", "Item2", "Item3", "Item4"
    # set up options' defaults
    gp.AddOptionInteger("Integer", intOption)
    gp.AddOptionDouble("Double", dblOption)
    gp.AddOptionToggle("Boolean", boolOption)
    listIndex = 3
    list = Rhino.Collections.Point3dList
    # WHA?
    opList = gp.AddOptionList("List", listValues, listIndex)
    pl = Rhino.Geometry.Polyline()
    while True:
        # perform the get operation. This will prompt the user to
        # input a point, but also allow for command line options
        # defined above
        for i in range(0,5):
            get_rc = gp.Get()
            #if you don't comply with the next if loop keep the command open
            if gp.CommandResult()!=Rhino.Commands.Result.Success:
                return gp.CommandResult() 
            #the if loop that completes the command
            if get_rc == Rhino.Input.GetResult.Point:
                
                point = gp.Point()
                scriptcontext.doc.Objects.AddPoint(point)
                scriptcontext.doc.Views.Redraw()
                #print "Command line option values are"
                #print " Integer =", intOption.CurrentValue
                #print " Double =", dblOption.CurrentValue
                #print " Boolean =", boolOption.CurrentValue
                #print " List =", listValues[listIndex]
                print "x= ",point.X,", y= ",point.Y,", z= ",point.Z
                pl.Add(point.X,point.Y,point.Z)
                print pl.Length
                #print list
                
                
                # 
            elif get_rc==Rhino.Input.GetResult.Option:
                if gp.OptionIndex()==opList:
                    listIndex = gp.Option().CurrentListOptionIndex
                    Rhino.Geometry.Polyline(list)
                    
                continue
        break
    return Rhino.Commands.Result.Success


if __name__ == "__main__":
    zPolyline()

I know I have a lot to learn, but still reusing pieces of code should be relatively easy. Replacing a thing or two. If a Line requires two points and can be defined as Line(startPt,endPt), then why can’t a Polyline be defined from a list like Polyline(list)? It requries point3dlist, ok, but when I add a point to it as point3dlist.Add(pt.X,pt.Y,pt.Z) it slaps me in the face with an error that there’s one more argument to be given. According to the api this should be “self”… :face_with_symbols_over_mouth: What is going on?

Thanks in advance.

EDIT: Is there plan in the future to abandon pythonscript and stick only with RhinoCommon, because in many examples both are used and this adds to the confusion.

That isn’t a point, that looks like three numerical values. :wink:

I’m not familiar with the python syntax, but in C# I would have written

pl.Add( new Point3d(point.X, point.Y, point.Z) );
or just
pl.Add( point );

// Rolf

It’s an accepted overload for Polyline.Add(), and unfortunately the first one that the Intellisense suggests.

On that same page you can see there’s a constructor for making a polyline out of IEnumerable<Point3d>, which really means any iterable of Point3d’s, not only a Rhino.Collections.Point3dList. It will happily take a Python list, tuple or even dictionary (as unsorted keys) as its argument.

2 problems here: you’re setting your variable to the class object itself instead of an instance of the class, hence the unhelpful errors about missing ‘self’ arguments. It’s a common source of confusion for Python beginners, just google around to learn more. python - When instantiating a class, what is the difference between with and without parentheses/brackets? - Stack Overflow
Also use another variable name - here you’re shadowing the built in list function.

I stand corrected. I shouldn’t have commented on anything python. :slight_smile:

// Rolf

It’s a RhinoCommon method overload so it applies to C#/VB.net also :slight_smile:
Could save a bit of typing if you’re constructing points directly from their coordinate values, instead of having to call new Point3d()

3 Likes

BTW …
rhinoscriptsyntax is a good example about how to use RhinoCommon IMO

On this PC I find it in

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

Or online:

Regards

No. Rhinoscriptsyntax is a higher level set of routines that are easier to use and understand, the Help for them is actually very well documented with sample code. Rhinoscriptsyntax methods actually call RhinoCommon methods “under the hood”, but often one Rhinoscriptsyntax method (one line of code) can substitute for dozens of lines of RhinoCommon code.

One important difference in approach between the two is the following:

Rhinoscriptsyntax methods normally work with GUIDS, that is to say the take references to existing objects from the active document in the form of GUIDS, and, when new objects are created, they are automatically written to the document and the new object GUIDs returned.

RhinoCommon normally works on “virtual” geometry that is not in the document. When one has to get geometry from existing objects in the document, this needs to be done explicitly by first getting the object’s ID and then extracting its underlying geometry. The reverse is also true, if you have RhinoCommon “virtual” geometry that you want to be in your Rhino file, it has to be explicitly written to the document - which while doing so automatically creates the object(s) GUID(s).

The interface between RhinoCommon and the active document is handled by the scriptcontext module.

So the rhinoscriptsyntax method for adding a line to the document looks like this:

line_ID=rs.AddLine(start, end) (where start and end are 3D points)

And the line will appear in the document immediately.

The Rhinocommon method looks like this:

line=Rhino.Geometry.Line(start, end) (where start and end are 3D points)

but ‘line’ is just a virtual line, and will not be added to the document unless you specifically do so in another step:

line_ID=scriptcontext.doc.Objects.AddLine(line)

–Mitch

2 Likes

Such virtual objects is a good thing if meant to be used only as a temp object in an algorithm, given the slowness with which Rhino bakes geometry to the Rhino document.

// Rolf

@Helvetosaur oh. That is very good thing to have in mind, thanks.

Now I understand why I am able to get the length of the polyline but not seeing the actual line in Rhino. This is truly helpful to have virtual objects, so you can create a lot of objects between your input and output.

I thought RhinoCommon was more powerful and having both APIs is just a left over due to switching to .net. I thought they are just doing the same thing using different syntax. That’s what confused me.

@qythium, thanks for the reference link. It is very helpful. I am usually jumping from language to another then spend some time off any programming, then I come back on another language and things become a mess. Each language has its own peculiarities.
Python2.x, Python3.x and IronPython, and Cython, they are different to me. In python in particular this leads to mindboggling confusion in my head:

def func():
    something = something_new
return
if __name__=="__main__":
func()

What ‘name’ what ‘main’ it’s a nightmare. Python is good that I don’t have to declare types explicitly, but these kinds of structures for non-programmer, oh man. I use it as it is without really understanding it. Around the web I just find in forums trashing beginners for even asking for such explanation.

EDIT:
Made a few changes but still cannot get the polyline in rhino. What’s even worse is I don’t get any errors to give me an idea where to look for the problem.

https://gist.github.com/zooid/58a863b3bfb9798d08ac94eb0d451e9f

Why is it that when I change the integer option the command ends after the first click (point)?
I have:

numPoints = 2
numPointsMin = 1
numPointsMax = 99
# set up the options
intOption = Rhino.Input.Custom.OptionInteger(numPoints, numPointsMin, numPointsMax)

then:

list = Rhino.Collections.Point3dList(numPoints)

while True:
    # perform the get operation. This will prompt the user to
    # input a point, but also allow for command line options
    # defined above
    for i in range(0,numPoints):
        get_rc = gp.Get()
        #if you don't comply with the next if loop keep the command open
        if gp.CommandResult()!=Rhino.Commands.Result.Success:
            return gp.CommandResult() 
        #the if loop that completes the command
        if get_rc == Rhino.Input.GetResult.Point:
            point = gp.Point()
            sc.doc.Objects.AddPoint(point)
            list.Add(point.X,point.Y,point.Z)
            sc.doc.Objects.AddPolyline(pl)
            sc.doc.Views.Redraw()

OFF-TOPIC: Any idea how can I embed github gist inside this forum?

It is as it allows you to have direct access to a lot of the lower-level functions that rhinoscriptsyntax does not. As you saw, it also allows you to create and manipulate geometry virtually without adding/deleting objects from the document, which can save a lot of runtime.

def func():
    something = something_new
    return
if __name__=="__main__":
    func()

As python scripts can be used as both main scripts as well as be imported as modules in others, the if __name__=="__main__" simply tells pyton to run the script if it is the main function, otherwise not. But it is also not necessary:

def func():
    something = something_new
    return something_new
func()

The above works perfectly well to run scripts.

As far as your point-picking problem goes, you could just use rs.GetPoints() which allows you to draw lines or not (first argument).

GetPoints(draw_lines=False, in_plane=False, message1=None, message2=None, max_points=None, base_point=None)

If you want to see the RhinoCommon code this calls, you need to look in

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

and open the “userinterface.py” file, scroll down about halfway to find the RhinoCommon code for GetPoints()… It’s about 50 lines of code which Rhinoscriptsyntax does in one line…

–Mitch

FWIW

writing this

pl.Add(point)

instead of this

list.Add(point.X,point.Y,point.Z)
sc.doc.Objects.AddPolyline(pl)

the script seems to work,
that is it draws a new polyline for every point added

hmm, for me it doesn’t

Edit:
on the other hand I might’ve screwed something messing around with the integer option.

So using this is only beneficial if I am going to use this particular .py as module included in another script?

So, it’s just how I intended it. I called this particular script command_template, because I would like at the end to call functions from other scripts inside it and in a way generating the shape of the command options depending on what function I call (or method, I don’t really differentiate between them). Theoretically it should be possible, but first I need to understand how this works. No success so far. :slight_smile:

Sorry.

It is

pl.Add(point)

instead of

list.Add(point.X,point.Y,point.Z)

But this line

sc.doc.Objects.AddPolyline(pl)

must be kept.

Thanks @emilio, funny result :slight_smile: creating NEW polyline at each click, but at least now I have a visual result to work with.

Yes.

To be quite honest, I never use it this way - but then I am just a script hack and not a programmer. So all the modules I need are explicitly in each script I write, I never call anything that is non-standard. I do this for one main reason - if I hand out scripts to someone else, I want them to run without a problem. If a script references an external module that you wrote but that the user doesn’t have and the script can’t find, it will fail.

–Mitch

Yes :slight_smile: , this is because at any iteration you add a polyline to the Rhino document.
To avoid this you might store the Guid of the previous polyline and delete that object when drawing the new one.
This might require a little time if the polyline grows enough …
( This one is the simpler way IMO )

Or you can override the OnDynamicDraw() method of GetPoint :

http://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Input_Custom_GetPoint_OnDynamicDraw.htm

here is some sample code from RMA :

( More complex way )

Or perhaps I should simply move sc.doc.Objects.AddPolyline(pl) out of that loop.

Sure.

( I thought you wanted to see the polyline while picking the points )

well yes, thanks for the tips,

I want to make this command work, so whatever happens during will help me learn how it works. Being it strange or not. :slight_smile: