Custom Lasso Selection



  • On 06/07/2016 at 06:44, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R13 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    Hi,

    Does anyone know how to make their own lasso selection tool?
    I think I can figure out how to draw the lines on the screen myself. But how do I handle the part where the object's, points, polys, edges, get selected within the drag area?

    I suppose I will need different code for each of these: objects, points, polys, edges?
    Anyone ever do this before?

    I've already written a radius selection type tool using a spherical radius to select the nearest point. But I'm not sure how to handle a lasso.

    -ScottA



  • On 06/07/2016 at 13:52, xxxxxxxx wrote:

    Use ViewportSelect. Then write your own rasterizer to go over every pixel in your selection area and check each one with GetPixelInfoPoint, GetPixelInfoPolygon, GetPixelInfoEdge, depending on your needs. These return a ViewportPixel which you can iterate over to find all objects/points/edges at that pixel location.

    There is a PickObject example in the SDK.



  • On 06/07/2016 at 14:30, xxxxxxxx wrote:

    Hi,

    I've got all that stuff under my belt. Using ViewportSelect and converting screen space to world space. All of that stuff I've used before in my other selection tool plugins.

    I'm storing the mouse positions while LMB dragging in an array.
    This gives me a list of points that form a virtual shape. And I can get the positions for each point in the objects.
    Now I need to somehow determine if the points of the object are within that array of points.
    I think I need some sort of math equation for determining what is inside and what is outside my array of points.
    No Idea if I'm even approaching this correctly?

    For starters. I'm concentrating on just selecting points.

    -ScottA



  • On 06/07/2016 at 14:55, xxxxxxxx wrote:

    Hey Scott,

    I thought you must have known most of that already.

    From what I am understanding you have drawn a shape on screen by recording the 2D positions of every point on the screen. Now what you would need to do is use a typical 2D rasterization algorithm to effectively "draw" a filled 2D image on screen. But instead of drawing anything at the x,y coordinate you are just calling the viewportSelect to determine what object is at that pixel.

    If this is not what you are after then I am totally misunderstanding what you are doing.



  • On 06/07/2016 at 15:09, xxxxxxxx wrote:

    Hi Scott,

    What you need to do is check whether a given point lies inside a polygon (in your case defined by a polygon drawn by your lasso command)



  • On 06/07/2016 at 17:06, xxxxxxxx wrote:

    I'll see if I can figure it out. But if you have any code I'd appreciate seeing it salozo.
    It might help me get there faster. I'm math challenged.

    -ScottA



  • On 06/07/2016 at 20:30, xxxxxxxx wrote:

    The issue can be handled better with the built-in class.



  • On 07/07/2016 at 07:22, xxxxxxxx wrote:

    Hello everyone,

    nice discussion you had here.
    We'd just like to throw the LassoSelection class into the mix. To be honest, we haven't had much experience with this class here in SDK Support, yet. So we are probably not of much help here currently. But perhaps one of you would like to play with it nevertheless.



  • On 07/07/2016 at 09:22, xxxxxxxx wrote:

    Doh! I didn't know there was a class for it.
    I'll take a look at that today and see if I can figure out how to use it.

    I'm still trying to figure out the best way to compare the object's points with the drawn points.
    -Converting the mouse screen coords to world coords with bd->SW() gives strange values
    -Converting the object's points to screen coords might get very slow with a dense mesh
    -ViewportPixel seems like the wrong class to use for this...but I'm not sure

    Maybe the LassoSelection class will help.

    -ScottA

    P.S.- Thanks for the code salozo. If I don't use it here it might still come in handy some day.



  • On 07/07/2016 at 11:32, xxxxxxxx wrote:

    After some testing. The LassoSelection class makes this dead simple and does all the work.
    However. I'm getting some very strange results from the Test(x, y) function.
    It's either a bug. Or I'm using it wrong?

    //This code is used in the MouseInput() method of a tooldata plugin  
    //It draws a lasso selection in the editor window  
      
      
      LassoSelection *ls = LassoSelection::Alloc();  
      ls->Start(win, MOUSESELECT_FREE);  
      
      /////Test #1/////  
      //If the entire polygon is surrounded it outputs 1...otherwise outputs zero  
      //Works as expected  
      
      CPolygon poly = obj->GetPolygonW()[0]; //<---the target polygon  
      
      //Convert the poly's points into 2D screen space  
      Vector pa = bd->WS(poly.a);  
      Vector pb = bd->WS(poly.b);  
      Vector pc = bd->WS(poly.c);  
      Vector pd = bd->WS(poly.d);  
      
      //Test if the 2D a,b,c,d are inside the selection.The point c can be equal to d in case of a triangle  
      bool test = ls->TestPolygon(pa, pb, pc, pd);  
      GePrint(LongToString(test));  
      
      
      /////Test #2/////  
      //Test if (x, y) is inside the lasso selection  
      //Works...But getting strange numbers from it: 1,2,8,64,128,etc...  
      Vector point = obj->GetPointW()[poly.a];  //<---The target point in world space  
      Vector pointWS = bd->WS(point);           //Convert the point into 2D screen space  
      LONG x = pointWS.x;  
      LONG y = pointWS.y;  
      Bool test2 = ls->Test(x, y);  
      GePrint(LongToString(test2));       //Working. But getting strange numbers?! 1,2,8,64,128,etc...  
      
      ls->Free(ls);   //<--don't forget or you'll get memory leaks!!
    

    -ScottA



  • On 07/07/2016 at 13:43, xxxxxxxx wrote:

    I thought you were writing your own Lasso Selection which is why I didn't suggest the actual inbuilt LassoSelection class itself. If it does what you need then definitely use it.

    I noticed that here you were using the indices of the polygon in the TestPolygon test. You should use the actual screen space coordinates for these as well. Also convert them to world coordinates by multiplying against the matrix. Something like this

      
    Vector *pPoints = obj->GetPointW();   
    Matrix mg = obj->GetMg();   
    Vector pa = bd->WS(mg * pPoints[poly.a]);   
    Vector pb = bd->WS(mg * pPoints[poly.b]);   
    Vector pc = bd->WS(mg * pPoints[poly.c]);   
    Vector pd = bd->WS(mg * pPoints[poly.d]);   
    Bool test = ls->TestPolygon(pa, pb, pc, pd);   
    

    Regarding the ls->Test(x,y) function, you should ignore the exact value itself and treat it as a Bool. So its either Zero or not Zero.



  • On 07/07/2016 at 16:17, xxxxxxxx wrote:

    Hi kbar,
    I'm not using polygon indices. I'm using their coordinates.
    That part is working as expected for me.

    The Test(x,y) function though should return a Bool. Not a random value.
    Sure I can ignore it. But it's not right.
    It's acting like maybe a pointer somewhere is not given a value. And returning gibberish.
    I don't know if it's a bug. Or if it's my fault?

    -ScottA



  • On 07/07/2016 at 19:13, xxxxxxxx wrote:

    C4D sometimes internally uses the Bool to return additional information, which is why Bool is actually defined as an int and not a bool. But as far as the outside world is concerned you just treat it as true or false.

    Will let the SDK guys handle it :)



  • On 08/07/2016 at 09:11, xxxxxxxx wrote:

    @Kent: Actually we are not sad at all, if you do our job 😉

    @Scott: It's exactly as Kent said, internally Bool is (yet) an integer, so it can return more than just true and false. And this is still sometimes used for historic and internal reasons. But you (in the sense of every 3rd party developer) really should handle it as a Bool, so you don't run into troubles, when this might change at some point in future. So just check the return value of Test() on (in-)equality to zero.



  • On 08/07/2016 at 09:34, xxxxxxxx wrote:

    Ok. Thanks.
    I just wanted to be sure I wasn't doing it wrong.

    Although targeting a specific point with the Text(x,y) is working (aside from the random int values).
    I'm having a hard time getting it to work properly on each point, of each object, in the scene.
    I'm using a C4DObjectList to grab all the objects. And then I'm using LassoSelection code similar to what I posted on them. But I'm getting inconsistent results.

    This is going to be a fight. 😠
    But I'll keep working on it.

    -ScottA



  • On 09/07/2016 at 10:34, xxxxxxxx wrote:

    Can you please ask the developers about this?
    The TestPolygon() function is not working properly for me in the Parallel & Perspective views.

    //This code will generate a lasso selection in a tooldata plugin  
    //But the lasso selects polygons even if the lasso is not over them, when not using an orthographic view!! (top,bottom,left,right)  
    //Is there a bug in the TestPolygon() function? Or is there a special way to use it in the Parallel & Perspective views?  
      
    Bool MyTool::MouseInput(BaseDocument *doc, BaseContainer &data, BaseDraw *bd, EditorWindow *win, const BaseContainer &msg)  
    {  
      AutoAlloc<LassoSelection> ls;  
      ls->Start(win, MOUSESELECT_FREE);  
      
      BaseObject *obj = doc->GetActiveObject();  
      if (!obj) return false;  
      
      PolygonObject *p_obj = ToPoly(obj);  
      Vector *points = p_obj->GetPointW();  
      LONG polygonCount = p_obj->GetPolygonCount();  
      BaseSelect *polyS = p_obj->GetPolygonS();  
      BaseSelect *pointS = p_obj->GetPointS();  
      Matrix mg = p_obj->GetMg();  
        
      //Start off with nothing selected  
      polyS->DeselectAll();  
      
      for(LONG i=0; i < polygonCount; ++i)  
      {  
          CPolygon poly = p_obj->GetPolygonW()[i];  
      
          //Convert the points of the polygon to Screen space coords (only works in ortho views!!?)  
          //NOTE: The z component can be used to get the distance the point is away from the active camera  
          Vector pa = bd->WS(mg * points[poly.a]);  
          Vector pb = bd->WS(mg * points[poly.b]);  
          Vector pc = bd->WS(mg * points[poly.c]);  
          Vector pd = bd->WS(mg * points[poly.d]);  
       
          Bool test = ls->TestPolygon(pa, pb, pc, pd);  
          GePrint(LongToString(test));  
          if(test == true) polyS->Select(i);     //Select the polygons hit by the lasso  
          else continue;  
      }  
      
      DrawViews(DRAWFLAGS_ONLY_ACTIVE_VIEW|DRAWFLAGS_NO_THREAD|DRAWFLAGS_NO_ANIMATION);  
      SpecialEventAdd(EVMSG_UPDATEHIGHLIGHT);  
      
      return TRUE;  
    }
    

    Thanks,
    -ScottA



  • On 12/07/2016 at 13:37, xxxxxxxx wrote:

    FYI, I'll look into it as soon as possible.



  • On 12/07/2016 at 16:01, xxxxxxxx wrote:

    Thanks Andreas,
    I'm thinking that maybe the camera's matrix might need to be included in the mix somehow?

    Also - The clipping params. are not doing anything.
    I tried setting them to all kinds of values, and no matter what values I used it does nothing. Confused

    -ScottA



  • On 15/07/2016 at 08:36, xxxxxxxx wrote:

    Hi Scott,

    I looked into it, the answer won't make you happy.

    First of all, in my tests, I can't say it doesn't work at all in perspective view. But I agree, it sometimes selects polys the lasso wasn't actually drawn upon.
    The thing is by default TestPolygon uses the bounding box of the drawn lasso. This has historical reasons. Internally the function has an additional parameter to change this behavior, which unfortunately never surfaced to the SDK. So there's currently no way for you to change this behavior.
    The good news is, you did everything correctly 😉

    I filed a bug report on this topic.

    Regarding the clipping, I haven't had the time to look into it. Unfortunately I'll be on vacation until July, 26th and the others in the team are currently pretty loaded. So this will have to wait until I'm back, sorry.



  • On 15/07/2016 at 10:38, xxxxxxxx wrote:

    OK. Thanks. I had a feeling that this thing was buggered. 😠

    While I was waiting. I manged to write my own custom lasso tool from scratch.
    It's not as efficient as the one in C4D. Probably due to me not caching things as much.
    But it works. So I'll keep working on it rather than try to use the one in the SDK.

    -ScottA


Log in to reply