Atom on Mac not autocompleting when Rhino is not running?

Hey guys, I’ve got it all running, executed the terminal commands and have checked if all the packages are correctly installed.

I’m using Yosemite and Atom for Mac and installed the Rhino-Python package correctly as shown here:

Somehow for example when typing rs.Add it doesn’t show me the option to autocomplete to AddCircle, it doesn’t matter it I add more words. Also, it doesn’t show the bar at the bottom where it shows which parameters belong to the AddCircle command.

Can it be that Atom is buggy with Yosemite or are there known common issues with Atom and the Rhino autocomplete?

I am guessing this is because I didn’t use the ‘StartAtomEditorListener’ why do I have to have that active? Isn’t there a way so that I can script while Rhino isn’t running in the background?

Hi,
Yes, Rhino needs to be running and you need to run ‘StartAtomEditorListener’.

Okay, no workaround I guess. That’s ok.

Is it also possible that I post my tryout script here to ask why it isn’t working? I feel the dynamics are a bit different when using the external editor.

Sure… the main problem with external editors at this point is no debugger… --Mitch

Thanks, well, I’m probably doing it all wrong, but this is what I came up with my first try.

import rhinoscriptsyntax as rs

def SurfaceSplitter():
    SelectedSurfaces = rs.GetObjects("Select objects")
    if (SelectedSurfaces==None): return

    rs.UnselectObjects(SelectedSurfaces)
    rs.DuplicateSurfaceBorder(SelectedSurfaces)
    rs.MeshPolyline(SelectedSurfaces)
    rs.MeshToNurb(trimmed_triangles=False, delete_input=True)
    rs.LastObject(SelectedSurfaces)
    rs.DeleteObject(SelectedSurfaces)
#call function defined above
    SurfaceSplitter()

What I am trying to do is this macro:
! -DupBorder _Pause MeshPolyline _SelNone _MeshToNurb
_TrimTriangularFaces=_No _SelLast _Enter _SelPrev _Delete

OK, I don’t have time to comment too much now, there’s a lot of stuff that needs to be explained here. However - is the idea that you want to split a set of linear-edged quad surfaces into two triangular surfaces each? there may be a more straightforward way to do this without trying to mimic a macro with a script. A quad surface can be split along one of two diagonals - is there a preference for the longer or shorter of the two?

–Mitch

Hi Mitch, thanks, yes the shorter would be better in that way it doesn’t change the ‘visual’ of the surface too much.

The idea would be to split a four-corner surface into two triangular ones. The reason why is because I had planar surfaces on which I turned the points on and then moved the points so they are not planar anymore. In the end I want to make paper models of them and therefor I need planar surfaces too. I don’t want to go over each surface manually, in Grasshopper I could manage, but I’m now working with the Mac version since I bought that one so I would have to start scripting my way through to be able to put these surfaces in an array and perform the trick ‘en masse’.

OK, many things to cover here…

First, I’m going to take a different route to make your triangles. Instead trying to use MeshToNurb etc. I’m simply going to get the corner points and I’m going to create two new triangular surfaces with them, then delete the original surface. All you need to do then is figure out which direction to create the triangles according to the shortest diagonal. There are lots of levels of sophistication that you can reach here, but I’m going to do it simply with all rhinoscriptsyntax tools.

Since you know some Grasshopper, you may be familiar with some or all of the following. In Grasshopper you would do the same thing just by connecting components with wires, but it actually makes code similar to what follows in the background.

So the first thing you need to know here is the use of variables. Variables are containers in which you can store things, they can be objects, numbers, True/False values, etc. This is the great advantage of scripts over macros, you can store references to things for future re-use. Most rhinoscriptsyntax methods return something and the result can be stored in a variable. For example the line

bord=rs.DuplicateSurfaceBorder(srf)

adds the border curve to the document and stores the ID (reference to the new object) in the variable “bord”.

Now the return of a function can also be several objects which is stored as a list:

pts=rs.CurvePoints(bord)

will return a list of corner points from the border curve. Lists in Python look like this: [...] Counting starts at 0 (as in Grasshopper) so they will be stored in positions 0 through 4 (first/last points are repeated in closed curves). One can address individual elements of a list with the syntax my_list[i] where i is the index number you want to get at. So you will see references below such as pts[0], pts[1], etc.

The second thing to learn is looping. A loop is a repetitive series of operations, for example to apply a particular procedure to a whole group of objects, one at a time. In this case we’re using a “for” loop to work on each surface individually.

The last thing is conditionals - decision making based on some sort of data. In this case we’re checking the distance between two sets of corner points and creating the triangles along the short diagonal. This is done with the if-else statement.

The whole thing then looks like this:

import rhinoscriptsyntax as rs

def SurfaceSplitter():
    #limit your choice to surfaces, allow preselection
    SelectedSurfaces = rs.GetObjects("Select objects",8,preselect=True)
    if (SelectedSurfaces==None): return

    #rs.UnselectObjects(SelectedSurfaces) #you don't need this
    #we're going to temporarily suspend redrawing of the screen (goes faster)
    rs.EnableRedraw(False)

    #we need to create a loop in order to work on each surface individually
    for srf in SelectedSurfaces:
        #get the border
        bord=rs.DuplicateSurfaceBorder(srf)
        #get the corner points (there will be 5, first/last are repeats!)
        pts=rs.CurvePoints(bord)

        #check diagonals, find shortest, create triangular surfaces
        if rs.Distance(pts[0],pts[2])<rs.Distance(pts[1],pts[3]):
            tri1=rs.AddSrfPt([pts[0],pts[1],pts[2]])
            tri2=rs.AddSrfPt([pts[0],pts[2],pts[3]])
        else:
            tri1=rs.AddSrfPt([pts[0],pts[1],pts[3]])
            tri2=rs.AddSrfPt([pts[1],pts[2],pts[3]])

        #delete unneeded objects
        rs.DeleteObject(srf)
        rs.DeleteObject(bord)

        #now loop on to the next surface, until all have been done
SurfaceSplitter()

You can go a lot further, for example checking and excluding surfaces that have more than 4 points or non-linear edges, joining the triangles to form a polysurface, etc… But this is good for a start…

Hope this small example helps… --Mitch

Hi Mitch, thanks a lot. I can now see what I did and didn’t get. I have experience in PHP/MySQL programming and also some basic other languages.

This was new to me, but very useful.

I’m still figuring out where I can put ‘IsSurfacePlanar’ because if a surface is planar, I don’t want the script to divide it. This way I don’t have to judge by eye, but I can simply select all the surfaces and then let the script choose the non-planars to work with.

I think I have fixed it, was this the correct way to do it?

import rhinoscriptsyntax as rs

def SurfaceSplitter():
    #limit your choice to surfaces, allow preselection
    SelectedSurfaces = rs.GetObjects("Select objects",8,preselect=True)
    if (SelectedSurfaces==None): return

    #rs.UnselectObjects(SelectedSurfaces) #you don't need this
    #we're going to temporarily suspend redrawing of the screen (goes faster)
    rs.EnableRedraw(False)

    planars = rs.IsSurfacePlanar(SelectedSurfaces)
    if (planars==False):
        #we need to create a loop in order to work on each surface individually
        for srf in SelectedSurfaces:
            #get the border
            bord=rs.DuplicateSurfaceBorder(srf)
            #get the corner points (there will be 5, first/last are repeats!)
            pts=rs.CurvePoints(bord)

            #check diagonals, find shortest, create triangular surfaces
            if rs.Distance(pts[0],pts[2])<rs.Distance(pts[1],pts[3]):
                tri1=rs.AddSrfPt([pts[0],pts[1],pts[2]])
                tri2=rs.AddSrfPt([pts[0],pts[2],pts[3]])
            else:
                tri1=rs.AddSrfPt([pts[0],pts[1],pts[3]])
                tri2=rs.AddSrfPt([pts[1],pts[2],pts[3]])

            #delete unneeded objects
            rs.DeleteObject(srf)
            rs.DeleteObject(bord)

        #now loop on to the next surface, until all have been done
SurfaceSplitter()

Well, not exactly. I think the problem is you’re looking at this a little as if it worked like Grasshopper. In Grasshopper the components are intelligent, and they create their own implied loops if you connect a list of objects to them. Python/Rhinoscript methods are usually designed specifically to take lists or not, there are a few that can deal with either, but not all. If the method does not accept a list, you need to create your own loop manually. The Help will tell you what each method takes for input.

IsSurfacePlanar() takes one surface ID at a time - otherwise it might be named AreSurfacesPlanar()…

Notice also that it can accept a second argument - tolerance - but that argument is optional. That means it will use a default value if none is supplied.

So, the logical place is to put this method inside the loop:

    for srf in SelectedSurfaces:
        if not rs.IsSurfacePlanar(srf,tol):  #tol is optional
            #get the border
            bord=rs.DuplicateSurfaceBorder(srf)
            #get the corner points (there will be 5, first/last are repeats!)
            pts=rs.CurvePoints(bord)
            #etc.

Everything under the IsSurfacePlanar() line gets indented one more notch. So, it loops through all the surfaces, test first to see which is planar, and if the surface does NOT pass the test, the surface is split.

There is also a little subtlety here, the True or False is implied. The statement if A is the same as if A==True; the converse if not A is the same as if A==False. However, you will also see that if A will test true if A has any value other than 0, False or None. It’s one of the python specialties that is worth noting and is actually quite useful.

HTH, --Mitch

Hey thanks Mitch, I think you’re right. My mistake was that I also tested my script on one selected surface and not on a group of surfaces, so I didn’t notice the problem you are describing.

Your help is much appreciated and I will surely go on with learning RS! As a lot of projects I do can be simplified by using scripts and I’ve always been macroing my way out of things, it seems logical for me to use. We all know that in the end this way of working probably causes you to spend more time on the scripting than you initially had spend if you had just done it manually, but that’s ok :wink:

In PHP it is the same with if A and A==Value it will mean the same.

I get it now, putting the planar check in the ‘for’ loop will loop it, and I have put it outside of that loop in the initial sketch.