Pick points and lines drawn in a Conduit


#1

Hi everyone,

I’m using a conduit to draw some geometry, and want to be able to select some of what I’ve drawn.
I’ve seen other plug-ins where it is possible, for example, to select mesh vertices or edges.
I want to do something similar, but with points and lines.

What’s the easiest way to do this? Can anyone point me to samples of code for achieving this or similar object picking operations?

Thanks in advance.


#2

Bump.


(Menno Deij - van Rijswijk) #3

I haven’t tried this myself, but what you need to do is convert picked screen coordinate to a world coordinate. Using the world coordinate you can find the geometry that is close to or at that coordinate and perform a “selection” action in your display conduit.

HTH,
Menno


(Dale Fugier) #4

Hi Stuart,

Rhino’s object picking support only extends to objects that are in the document. In other words, the Rhino SDK does not have pre-canned functions that you help you pick dynamically drawn geometry. That said, it is possible to simulate the picking of dynamic drawn geometry. Rhino 5’s Gumball object is a good example of this.

The first think you will need to do is maintain a list of the dynamically drawn geometry. Without one, there is no hope of ever knowing what you picked on.

The next think you will need to do is to construct a pick context (Rhino.Input.Custom.PickContext). Having one of these will provide whatever function you write, that does the actual picking, some useful information on where and how the user picked. I don’t find a lot of examples of using a pick context (because it is hard). But I do have an example of using a custom Gumball object that uses a pick context. Here is the sample of how to construct one:

https://github.com/dalefugier/SampleCsGumballCylinder/blob/master/SampleCsGumballCylinderGetPoint.cs

The real work is in your own picking function, where you get to interpret what is provided by the pick context and see if it hits any of your custom geometry.

Hope this points you in the right direction.


#5

Hi chaps,

Thanks for the suggestions and samples. I have explored both options a bit, although found by doing so that they converged towards the same sort of thing.

Based on the gumball example that Dale provided, I have tried to set up my own pick context as below:


Public Class ConduitPointGetter
Inherits GetPoint

Private points As Point3d()
'The points that are drawn in my conduit, passed to this class when I use it
Public Property PointList() As Point3d()
    Get
        Return points
    End Get
    Set(ByVal value As Point3d())
        points = value
    End Set
End Property

Protected Overrides Sub OnMouseDown(e As Rhino.Input.Custom.GetPointMouseEventArgs)
    Dim Picker As New Rhino.Input.Custom.PickContext()
    Picker.View = e.Viewport.ParentView
    Picker.PickStyle = Rhino.Input.Custom.PickStyle.PointPick

    Dim xform As Transform = e.Viewport.GetPickTransform(e.WindowPoint)
    Picker.SetPickTransform(xform)

    Dim index As New Integer
    Dim dpth As New Double
    Dim dis As New Double

    Picker.PickFrustumTest(Me.PointList, index, dpth, dis)
End Sub

End Class


I’ve done this on the assumption that

Picker.PickFrustumTest(Me.PointList, index, dpth, dis)

is a way of comparing the point I’ve clicked with the points in my conduit (which I pass to the class by setting the property where I call the method). My assumption however doesn’t correlate with the overloads of PickFrustumTest. Unfortunately there’s some documentation missing in that bit of the SDK.

Before I tried the pick context suggestion, I used Rhino.Input.Custom.GetPoint to get a 3D point, and then, by comparing the distance between that point and all the points in my conduit, figured out which point it was. I also found that I could use GetPoint.ClearSnapPoints and GetPoint.AddSnapPoint/s to ensure that the cursor would only allow me to click on the location of the drawn points. (I could do this with my class above too).

What I can’t immediately see is how to use the pick context that I’ve ended up with. Similarly I can’t figure out how either method could allow me to select multiple drawn points by dragging a rectangle around them (as I could do with points actually in the document).


(Alain Cormier) #7

Hi,

I created a sample that shows how to select points in a conduit:
http://wiki.mcneel.com/developer/rhinocommonsamples/pickobject

I tried to do the bare minimum to show the concept and nothing more.
Let me now if this helps.


#8

I think you should make a polyline that will close on right mouse button or enter. Then selboundary the polyline. After this you will have everthing selected within the polyline


#9

Brilliant, that’s made it crystal clear.

Thanks very much everyone!


#10

As mentioned above, the example by Alain was posted in C# and VB: https://wiki.mcneel.com/developer/rhinocommonsamples/pickobject. Also found here: http://developer.rhino3d.com/samples/rhinocommon/pick-points/

Apparently, he also posted it in Python here: https://github.com/mcneel/rhinocommon/blob/master/examples/py/ex_pickobjects.py

I had to make some small changes to make it work for me. Changes indicated as annotations in the code:

import System.Drawing
#added import Rhino
import Rhino
import Rhino.Input.Custom
from scriptcontext import doc

def RunCommand():
  conduitPoints = []
  conduit = PointsConduit(conduitPoints)
  conduit.Enabled = True

  gp = Rhino.Input.Custom.GetPoint()
  result = Rhino.Commands.Result.Success

  while True:
    gp.SetCommandPrompt("click location to create point. (<ESC> exit)")
    gp.AcceptNothing(True)
    gp.Get()
    result = gp.CommandResult()
    if result != Rhino.Commands.Result.Success:
      break
    conduitPoints.append(ConduitPoint(gp.Point()))
    doc.Views.Redraw()
    
  gcp = GetConduitPoint(conduitPoints)
  result = Rhino.Commands.Result.Success
  
  while True:
    gcp.SetCommandPrompt("select conduit point. (<ESC> to exit)")
    gcp.AcceptNothing(True)
    gcp.Get(True)
    doc.Views.Redraw()
    if gcp.CommandResult() != Rhino.Commands.Result.Success:
      break
    
  return Rhino.Commands.Result.Success

class ConduitPoint():
  def __init__(self, point):
    self.Color = System.Drawing.Color.White
    self.Point = point

class GetConduitPoint(Rhino.Input.Custom.GetPoint):
  def __init__(self, conduitPoints):
    self.conduitPoints = conduitPoints

  def OnMouseDown(self, getPointMouseEventArgs):
    #picker = PickContext()
    picker = Rhino.Input.Custom.PickContext()
    picker.View = getPointMouseEventArgs.Viewport.ParentView
    
    #picker.PickStyle = PickStyle.PointPick
    picker.PickStyle = Rhino.Input.Custom.PickStyle.PointPick

    xform = getPointMouseEventArgs.Viewport.GetPickTransform(getPointMouseEventArgs.WindowPoint)
    picker.SetPickTransform(xform)
    
    #for cp in conduitPoints:
    for cp in self.conduitPoints:
      b, depth, distance = picker.PickFrustumTest(cp.Point)
      if b:
        cp.Color = System.Drawing.Color.Red
      else:
        cp.Color = System.Drawing.Color.White

class PointsConduit(Rhino.Display.DisplayConduit):
  def __init__(self, conduitPoints ):
    self.conduitPoints = conduitPoints
    
  def DrawForeground(self, drawEventArgs):
    
    #for cp in conduitPoints:
    for cp in self.conduitPoints:
      #drawEventArgs.Display.DrawPoint(cp.Point, PointStyle.Simple, 3, cp.Color)
      drawEventArgs.Display.DrawPoint(cp.Point, 0, 3, cp.Color)

if __name__ == "__main__":
  RunCommand()