Mouse-over point OR the Object - why not both?


#1

I’m trying to write a script where mouse-over movements displays which current OSnap alternative would apply if clicking the object at that moment in time. The code:

 rc, pt_start = Rhino.Input.RhinoGet.GetPoint("Start point", False)

This displays what I want (the current OSnap), because seeing/knowing in advance which point to snap to is exactly what I want. But, it seems that, from the above command I can only get the point (pt_start) but not access to the underlaying object itself.

I could use the following get-method instead so as to get access to the object, but then I don’t see the fancy mouse-over Osnap display, and thus not being able to determine if a click would hit the desired Osnap point (End or Mid, Cen etc):

obj_start = rs.GetObject("Start point", filter=(...), select=True)

Q: So how can I have both the Mouse-over/OSnap display and get access to the underlaying clicked object as well as determining which “OSnap point” on the object was selected?

// Rolf


#2

Does rs.GetObjectEx do what you want?

Edit: Hmm, guess not, no object snap… Oh well, too fast there.


#3

Well, a brute force method would be to get the point and then iterate through all of the objects in the scene and find out which object the point is on… --Mitch


#4

There is:

rs.PointClosestObject (arrPoint, arrObjects)

method, but the point could be part of multiple objects.

c.


#5

Oh, yeah, thanks for that.

Yep, of course.

–Mitch


#6

Right, the OSnap is missing. I had spotted this one (the sweet spot at Element 3) and hoped that it would display the OSnap, but regrettably it doesn’t.

I suspect that the brute force method is at risk of being very slow on big models, and (in Bongo animations) I often have like five points or more occupying the exact same 3D location (Pivots often ends up having the same location). So in the code I wouldn’t always be able to determine which object/point is the intended one. :frowning:

Hm. I’ll try this one, but it may suffer from a similar problem as mentioned above.

Edit: Hm, I just got the idea that if multiple objects matched, one could simply switch mode to the usual explicit object picking, or directly displaying the same list as is usually displayed when multiple objects occupy the same point. Picking from that list (currently only with the mouse) also with the TAB key would be a reasonable solution.

In any case, thank you for your suggestions, it’s appreciated. And if no existing function does the trick, we may have to bribe the McNeel guys to make a dirty hack… :slight_smile:

// Rolf


#7

@clement & @Helvetosaur,

Thinking of combining both your suggestions, traversing candidate objects, and doing so using the rs.PointClosestObject command. But this command requires a bunch of objects (point + array parameter) prior to selecting a point.

However, I do think that the problem could be solved if populating the array of objects adjacent to the picked point AFTER the GetPoint command (thus keeping the Osnap behavior). Something like the following (but I don’t know how to code it) :

  • Given the picked Point from rs.GetPoint, one could mimic a cross-window selection, a square of say 1x1 or 2x2 pixels around the picked point, and then assign the selected objects to an array. Then the array could be used by rs.PointClosestObject (and thus the then the number of objects to select from, if more than one, would be limited and not suffer from large numbers of objects in the model). And the OSnap behavior would be there directing the whole thing from start.

Now to figure out if it’s possible to mimic the cross window from code… :slight_smile:

// Rolf


#8

@RIL, how about creating a small sphere at the picked point and find out which of your potential geometries do intersect ?

btw. if you know the geometries in question, eg. curves, you could also use rs.GetPointOnCurve and force the user to do the object selection first, followed by a point pick…

c.


#9

That would definitely be an option. If not checking for intersect the PointClosestObject seems to pick any nearby object even if it’s not intersecting.

This, OTOH, is precisely what I’m trying to avoid. I want to do only one pick, and then let the code figure out which (nearest) object to select, and snap to the nearest Osnap point on that object. :slight_smile:

// Rolf


#10

In the Rhino WIP, the Rhino.PointPick method has been enhanced by Dale to allow for Osnaps ( see RhinoScript help file for full description ). Not sure if/when Python version will be updated, but it seems like that could help with what you are trying to do.
Once you get the list of all objects under the cursor and their respective pick points, you can sort them by camera distance to make an intuitive pick of what the user sees and go from there.

–jarek


(Steve Baer) #11

You’ll want to use the full blown Rhino.Input.Custom.GetPoint class for this. Once you have a point, you can call PointOnObject to see if the point was associated with an object

http://developer.rhino3d.com/api/RhinoCommonWin/html/M_Rhino_Input_Custom_GetPoint_PointOnObject.htm


#12

Ooh, that’s a good one… :smile: --Mitch


#13

@stevebaer,

i’ve thought about that too but it gets pretty hard to determine on which object the point has been picked, eg. in case you have 3 curves starting at a point and that point is picked.

import Rhino
import scriptcontext
import rhinoscriptsyntax as rs
    
def DoSomething():
    
    gp = Rhino.Input.Custom.GetPoint()
    gp.SetCommandPrompt("Pick a point")
    get_rc = gp.Get()
    if gp.CommandResult() != Rhino.Commands.Result.Success:
        return
    
    print gp.Point()
    objref = gp.PointOnObject()
    if objref: 
        rs.SelectObject(objref.ObjectId)
    
DoSomething()

Looks like it takes the curve which is closest to the cursors crosshair center.

c.


#14

Yes @clements, not being able to determine which object is a showstopper. The other command ( Rhino.Input.RhinoGet.GetPoint ) at least went half ways to where I want to go since it hightlights the object being hovered over. But also that good thing is useful only if the objects are not entirely overlapping (like points). Rhino handles cases like this by showing a popup list with the underlaying objects (if I could do something similar with script the problem would be handled).

I’ll see if I can find a solution to this later. So far I had used the Rhino.Input.Custom.GetPoint() only for the End point, since the only thing that matters for the target point is the correct position, and not what other objects are located there.

"WHILE SHIFT"
I’d like to maintain a While loop while the SHIFT key is pressed. How do I read keyboard states?

I tried to find a way to read keyboard states but didn’t find any example in the help file (not even when grepping the sources. I’m obviously entirely at loss with Python…).

PROPERTY INSPECTOR
Same thing there, I want to activate the Object Property panel, and update it with the currently selected object for display. I found Rhino.UI.Panels(...) which takes an Id, but I couldn’t find a way to retrieve the Id for this particular panel so… there we go again. How?

// Rolf


#15

SMOVE

First of all, thank you all for your advice and code snippets, it really helps. This is an amazing group which is really inspiring!

Here’s my current premature version of SMove (a SmartMove alternative to the std Move command), which aims to demonstrate how the intended SMove command really should work. It needs more functionality as described in the header, but the basic idea is covered here.

CONTRIBUTE
Anyone with the self confidence is free to contribute (of course), but don’t forget to make a notice about new version and your signature. Then test it and upload to share. I will continue to work on it when I have code solutions for the feature list as described. Short version of SMove purpose:

SUMMARY

  1. Only one click to select Start point
  2. Mouse-over highlighting to indicate which object is hovered over.
  3. Osnap point highlighting indicating where on the focused object the snap will land.
  4. TAB-bing to toggle among available OSnap points (End, Mid, End, and back again etc)
  5. All this makes for a Faster, more Accurate, and it follows, Less Tiring command (due to less need for deep concentration).
  6. This model for selection in a command is applicable in many other commands as well, like Copy, Mirror, etc.

I would appreciate advice about how to configure the script so be available like any std Rhino command which can be repeated etc.

// Rolf


FYC: The main loop starts at the line “# START POINT” far below

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

# ------------------------------------------------------------------
# SMove - A customized Smart-Move command that picks a default start 
# point without extra clicks
# ------------------------------------------------------------------
# MORE INFO
# The SMove command must highlight the Mouse-over object AND the OSnap 
# point for the start point. Only one click to select the first object and 
# its position. The picked object(s) are added to selection. TAB should alter the 
# first picked OSnap point. A Line, for example, should toggle between either 
# End points through the Mid point. 
# A Solid should do likewise, but along its edges. CTRL-TAB could alter, 
# not the OSnap point, but the edge itself.
# -------------------------------------------------------------------
# Rev 0.1.0     - 2017-02-20 RIL
# Rev 0.?.?     - 2017-??-?? Signature
# Rev 0.?.?     - 2017-??-?? Signature
# Rev 0.?.?     - 2017-??-?? Signature
# 
# TODO:
# [ ] Restrict first pick point from selecting nearby objects when no object was 
#     actually hit (intersecting with a sphere as suggested by @clement is probably a good idea).
# [ ] Enable multiple objects to be picked (While loop while pressing SHIFT?), 
#     The first pick (if any) determine the starting point. 
#     If window-selected from start, then automagically revert to "old" 
#     standard Move command (for explicitly picking the start point).
# [ ] Allow select & Move of multi selection (as of 0.1.0 only one object 
#     can be moved)
# [ ] TAB-toggle OSnap point of selected (first) Start point (see MORE INFO).
# [ ] CTRL-TAB-toggle OSnap point to edge for solids.
# [ ] (Something similar to TAB-select for selecting surfaces of a solid?)
# [ ] Set first selected object in focus in Object Proterty Panel (or, output 
#     object name to status bar, or command line as info-tect? Other?)
# [ ] Add a rev-notice above for each addition.
#
# -------------------------------------------------------------------


def SMove():
    # PRE SET
    line_color = System.Drawing.Color.FromArgb(200,0,0) # Dynamic line color 
    startObj = None
    endObj  = None

    # DynamicDraw (dyn line from start pt to current mouse position)
    def GetPointDynamicDrawFunc( sender, args ):
        args.Display.DrawLine(start_pt, args.CurrentPoint, line_color, 2)
        args.Display.DrawLine(end_pt, args.CurrentPoint, line_color, 2)
        # DynamicDraw event

    #PICK START
    def PickStartPoint(msg, obj):
        res = False
        pt = None
        obj = None
        rc, pt = Rhino.Input.RhinoGet.GetPoint(msg, False)
        if( rc!=Rhino.Commands.Result.Success ):
            return
        # Get first object, and allow for changing start point:
        arrAll = rs.NormalObjects()
        if arrAll:
            arrClosest = rs.PointClosestObject(pt, arrAll) 
            if arrClosest:                
                obj = arrClosest[0] #obj = arrClosest[len(arrClosest)-1]
                if not obj: 
                    return
                elif rs.IsObject(obj):
                    rs.SelectObject(obj)
        if not obj:
            return res, pt, obj
        return True, pt, obj
        # PickObject()

    #PICK END
    def PickEndPoint(msg, obj):
        pt = None
        # Enable dynamic draw of line from start to mouse, an instance of a 
        # GetPoint class added as a delegate for the DynamicDraw event (also
        # later used as the End point):
        cpt = Rhino.Input.Custom.GetPoint()
        cpt.SetCommandPrompt(msg)
        cpt.DynamicDraw += GetPointDynamicDrawFunc
        cpt.Get()        
        # Object to point
        pt = cpt.Point()
        # No SelectObject for end point
        return True, pt, cpt

    # START POINT
    res, start_pt, startObj = PickStartPoint("Start point", startObj)
    if not res: 
        return

    # END POINT
    res, end_pt, cpt = PickEndPoint("End point", endObj)
    if not res: 
        return

    # MOVE
    if(cpt.CommandResult() == Rhino.Commands.Result.Success): #and (endObj):
        rs.MoveObject(startObj, end_pt-start_pt)
        ctx.doc.Views.Redraw()
        
if( __name__ == "__main__" ):
    SMove()

Wish: Improve the Move command
#16

Hi Rolf,

one thing ahead since i am unsure if you are aware of it: Rhino has a useful feature when dragging objects, eg. if you select one or more objects by picking near the point you want to move from and HOLD the mouse, the object snap shows up. If you now drag the mouse, the drag acts like a move from command, so the from point is using the snap which has been shown.

Apart from above, i am not entirely sure if i can follow all notes in your SMove command. But i’ve quickly hacked something together. It works similar as shown above, but now highlights the object under the mouse cursor before you pick the point. Once picked, it starts to move the highlighted object from the picked point.

Rolf_SMove_Prototype.py (1.8 KB)

Note that this prototype works with post-selection of a single objects only, and it only hightlights if the snaps are enabled. Therefore i prefer Rhino’s build in dragging which is quite powerful and more intuitive.

c.


#17

This is what I was referring to by “snap-dragging” in this thread… The advantage, especially with pre-selecting one object only, is that the “from” snap is guaranteed to be on the object, so if “end” shows up for example, you can be sure it’s the end of the curve object you have selected and not one just next to it.

–Mitch


#18

As far as I could see, you r version is much better than mine. You cover the basic idea perfectly, and with good highlightning, and OSnap works as desired, in your version also on the End point.

And your code also doesn’t pick “nearby” objects if hitting in empty space. Very good.

I’m not sure though if I understood, and therefore I don’t know if I’ve seen the behavior you mention in your first sentence. If I pick with the mouse only near, but not hit an object, the command terminates immediately (which I think it should).
If I select objects before running the command, the selection is released, and if I pick after starting the command, it immediately starts moving after first pick, and I don’t seem to be able to select any more objects than the first, or?
I guess I just didn’t get what you meant there. Can you elaborate?

In any case, if this command can be enhanced to Move multiple objects (and perhaps also tabbing around with the OSnap pick-point) then it’s very very good. The tabbing isn’t perhaps the absolute most important thing, but in some cases it would really make life easier.

Also a pick-list with objects stacked on top and covering each other (this is often the case for points, especially when working with Pivots in Bongo) is important.

Very interesting version this! I will study your code some more when I have the time, but this seems like a better start tyhan my code.

// Rolf


#19

Yes, thats how it is set up since i did not know how to handle preselection if no point was picked. Note that it is just a prototype and if the Near snap is turned off, sometimes it feels a bit strange since no object gets highlighted.

To go for multi selection and pre-selection the concept has to be tweaked with some extra rules. I have not been sure if adding to the selection works by just highlighting while SHIFT is pressed. You can try to add that via the GetPointMouseEventArgs function.

You can implement that if you know the objects near the picked point and get their description, eg. “Point”, “Curve”, “Surface” etc. and create the pick list in a rs.PopupMenu function. But i guess then you do not need the highlighting at all. Also note that rs.PopupMenu does only allow a single item pick and it will not highlight while moving the mouse over the menu as it happens in core Rhino.

c.


#20

Thank you very much for these hints! I will dive into it asap.

// Rolf