Get the matrix of selected polygons?



  • On 12/05/2014 at 13:37, xxxxxxxx wrote:

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

    ---------
    When we select more than one polygon. Cinema 4D creates a new matrix for them and displays an axis gizmo for it.
    Is there any way to grab this matrix and get it's values?

    The most important value I need from it is the position value (mtx.off).
    It's not just the average of the selected polygon's center positions. It's where the normals of the selected polygons all intersect. And I don't know how they're arriving at this position value.
    I need a way to get this value from the matrix. Or the code they used to calculate it.

    Thanks,
    -ScottA



  • On 13/05/2014 at 03:07, xxxxxxxx wrote:

    HI,

    not sure if there is a way to get this using C4D SDK, probably not.
    One slow workaround could be to create new PolygonObject and copy only selected polygons to it, then get Matrix from it.
    Another of course better way would be to calculate it by you self.

    > It's not just the average of the selected polygon's center positions.
    > It's where the normals of the selected polygons all intersect.
    Are you sure about this?
     I can not imagine how this can be true for any complex shape.

    If you want to calculate only mg.off then please do not use naive approach and simple sum all the points, this will work bad for many points.
    Please look at kahan-summation instate.
    http://shape-of-code.coding-guidelines.com/tag/kahan-summation/

    regards,
    Remo



  • On 13/05/2014 at 06:14, xxxxxxxx wrote:

    Howdy,

    Is this what you're needing from the BaseDocument class?

    [BaseObject](file:///Users/danlibisch/Documentation/C4D%20Manuals/R12%20SDK/pages/c4d_baseobject/class_BaseObject69.html)* GetHelperAxis(void)_<_h4_>_

    The helper axis for the current multi selection.<_<_h5_>_h5_<_h5_>_rn
    > [BaseObject](file:///Users/danlibisch/Documentation/C4D%20Manuals/R12%20SDK/pages/c4d_baseobject/class_BaseObject69.html)*
    >
    >> The axis object. The document owns the pointed object.
    >>
    >>
    >

    Adios,
    Cactus Dan



  • On 13/05/2014 at 08:05, xxxxxxxx wrote:

    @Dan
    I'm looking for the polygon equivalent of that.

    @Remo

    Originally posted by xxxxxxxx

    Are you sure about this?
     I can not imagine how this can be true for any complex shape.

    Well. Here's what I'm seeing.
    When I select two polygons in a cube. The axis gizmo that C4D creates is at vector(0,0,0).
    But when I rotate those polygons with the rotate tool. That axis will move around.
    I tried to make sense of where the axis was being move to. And what I'm seeing is that the axis is always being placed where the selected polygons normals intersect. Almost like they are using rays from the polygons to position this Polygon matrix.

    I created a small test setup where I calculated the center positions of these same selected polygons in a cube. And placed a sphere there. And the results are different from what Maxon is doing.
    If I use the polygon centers like that. The polygons will change scale(warp) when they are rotated.

    I have successfully created code that will let me rotate one polygon at a time. By using the kind of method you described Remo. But it doesn't work with multiple polygons, because in my loop I rotate each polygon per loop cycle. And when you do that, the connected edges will move in the next loop iteration when you don't want them to. And the polygons get warped.

    I think I can eventually solve the shared edges moving too many times problem.
    But a bigger problem is figuring out how Maxon is calculating the matrix position of selected polygons.
    I thought it would be fairly easy to rotate multiple selected polygons. But it's not so easy to rotate them without changing their scale or warping them.

    -ScottA



  • On 13/05/2014 at 12:09, xxxxxxxx wrote:

    I think I found it.

        BaseObject *obj = doc->GetActiveObject();  
      Matrix axis = obj->GetModelingAxis(doc);  
      GePrint(RealToString(axis.off.x) + " " + RealToString(axis.off.y) + " " + RealToString(axis.off.z));
    

    Does anyone know if I can use SetModelingAxis() to rotate my selected polygons?

    -ScottA



  • On 14/05/2014 at 10:56, xxxxxxxx wrote:

    This is what I've come up with to allow the user to rotate the selected polygons.
    I'm sure it's not 100% perfect. Because I'm doing some trickery in it. Like switching to points mode to avoid shared edges from rotating too often.
    And I don't know why the modeling matrix won't allow me to use it when rotating a single selected polygon. So I created a hack for that scenario that uses a temp object's matrix to rotate a single polygon.

    It seems to work fairly well. And I can probably get by with it. But I'd love it if anyone else has a better way to do this?
    The way I'm doing it seems a bit hackish to me.

    //This code example rotates the selected polygons  
      
      
    #include "..\..\..\..\resource\_api\c4d_libs\lib_selectionchanger.h"  
      
      
    Bool SimplePlugin::Execute(BaseDocument *doc)  
    {   
      //Cast the active object to a polygon type object and check if it's editable   
      PolygonObject *obj = (PolygonObject* ) doc->GetActiveObject(); if(!obj) return FALSE;  
      if(obj->GetType() != Opolygon) return FALSE;  
      if(doc->GetMode() != Mpolygons) return FALSE;  
      
      //First we select the Points of the selected polygons using a selection changer  
      BaseSelect *polySel = obj->GetPolygonS();         //Get the currently selected points of the object  
      LONG polyScount = polySel->GetCount();            //The number of selected polygons  
      if(polyScount < 1) return FALSE;  
      BaseSelect *pointSel = obj->GetPointS();          //Get the currently selected edges(this will probably be zero)  
      SelectionChanger *sc = SelectionChanger::Alloc(); //Create an instance of the SelectionChanger class  
      sc->InitFromSelection(polySel, Mpolygons, obj);   //Initialize it with the selected points and the object  
      BaseSelect *newsel = sc->GetPointS();             //Store the selected edges (based on selected points) in a BaseSelect  
      CallCommand(12139);                               //Switch to Edges mode  
      newsel->CopyTo(pointSel);                         //Make the edges selected on the object in the scene  
      obj->Message(MSG_UPDATE);                         //Update the changes  
      SelectionChanger::Free(sc);                       //Free the memory used in Alloc  
      
      //We can switch back to polygon mode now and still edit the selected points  
      CallCommand(12187);                            //Switch back to polygon mode  
      
      //Change theses values as desired to rotate the matrix  
      //Put these in your GUI for the user to use  
      Real rotX = 0.0;  
      Real rotY = 0.2;  
      Real rotZ = 0.0;  
      
      //We have to use a different method of rotation if there was only one polygon selected  
      //So we need to check if there's more than one polygon selected by the user  
      if(polyScount > 1)  
      {      
          //Get the selected points in the object and store them in a BaseSelect array  
          Vector *points = obj->GetPointW();  
          LONG pointCount = obj->GetPointCount();  
          BaseSelect *bs = obj->GetPointS();  
      
          //Gets the special modeling axis used when the point/polygons mode is active  
          Matrix mAxis = obj->GetModelingAxis(doc);  
      
          //Get the hpb value from the matrix  
          Vector hpb = MatrixToHPB(mAxis, ROTATIONORDER_DEFAULT);  
            
          //Rotate the matrix using the user's input  
          hpb += Vector(rotX, rotY, rotZ);   
      
          //Convert the HPB values back to a matrix again  
          mAxis = HPBToMatrix(hpb, ROTATIONORDER_DEFAULT);  
       
          //Apply the changes made to the modeling matrix  
          obj->SetModelingAxis(mAxis);  
      
          //Rotate each of the selected points  
          doc->StartUndo();  
          for(LONG i=0; i < pointCount; i++)  
          {   
              //If the point is selected then we will rotate it  
              if(bs->IsSelected(i))  
              {   
                  //Get the selected points vector positions  
                  Vector padr = obj->GetPointW()[i];  
      
                  //Rotate the selected points by multiplying them by the mAxis  
                  doc->AddUndo(UNDOTYPE_CHANGE, obj);  
                  points[i] = points[i] * mAxis;  
              }  
          }  
          obj->Message(MSG_UPDATE);   
          doc->EndUndo();  
      }  
      
      //If only one polygon was selected by the user we use a different method to rotate it  
      //Because for some reason. Using the same Modeling matrix to rotate the single polygon  
      //Makes it rotate way too much!!?  
      else  
      {  
          //Get the selected polygon's index#  
          //Since there's only one polygon selected...we can use the GetLastElement() function for this  
          LONG polyID = polySel->GetLastElement();  
          CPolygon *polys = obj->GetPolygonW();  
          Vector *points = obj->GetPointW();  
      
          //Get the index#'s for the four points in the selected polygon  
          LONG A = polys[polyID].a;  
          LONG B = polys[polyID].b;  
          LONG C = polys[polyID].c;  
          LONG D = polys[polyID].d;         
      
          //Get the vector positions of the four polygon's points  
          Vector posa = points[A];  
          Vector posb = points[B];  
          Vector posc = points[C];  
          Vector posd = points[D];  
          Vector center = (posa+posb+posc+posd)/4;  
      
          doc->StartUndo();  
      
          //Create a new (virtual) Null object in memory only  
          //Not physically adding it to the scene will make our code run faster  
          //We could have also created a new matrix..But it's easier to rotate a Null object  
          BaseObject *virtualNull = BaseObject::Alloc(Onull);  
          doc->AddUndo(UNDOTYPE_NEW, virtualNull);  
      
          //Then we create a new null object the axis is always created at 0,0,0  
          //So we have to move it to where the center of the selected polygon is  
          Matrix mtx = virtualNull->GetMg();  
          Vector dif = mtx.off - center;  
          mtx.off = center;  
          virtualNull->SetMg(mtx);  
      
          //Rotate the new virtual Null object as desired  
          virtualNull->SetAbsRot(Vector(rotX,rotY,rotZ));  
      
          //Rotate the selected polygon by transforming it's points in the Null's global space  
          //Multiplying the polygon's points by the Null's matrix makes them act like children of the Null Object   
          doc->AddUndo(UNDOTYPE_CHANGE, obj);      
       
          //Rotate the selected polygon by transforming it's points in the null's global space  
          //Multiplying the polygon's points by the null's matrix makes them act like children of the null   
          doc->AddUndo(UNDOTYPE_CHANGE, obj);   
          posa = (posa+dif) *  virtualNull->GetMg();  
          posb = (posb+dif) *  virtualNull->GetMg();  
          posc = (posc+dif) *  virtualNull->GetMg();  
          posd = (posd+dif) *  virtualNull->GetMg();  
      
          //The changes we've made have only been saved in the variables..We need to apply them to the points of the polygon  
          //NOTE: In Python we would need to use SetPoint() for this  
          points[A] = posa;  
          points[B] = posb;  
          points[C] = posc;  
          points[D] = posd;  
        
          obj->Message(MSG_UPDATE);      
          virtualNull->Remove();  //Delete the virtual Null Object we used to rotate the selected polygon  
      }  
      
      EventAdd();  
      return TRUE;  
    }
    

    -ScottA



  • On 20/05/2014 at 10:30, xxxxxxxx wrote:

    Scott,

    If the doc mode is not set to Polygons, you could set it to avoid bailing unnecessarily.

    Only do the AddUndo() call once (not in the loop).



  • On 20/05/2014 at 13:28, xxxxxxxx wrote:

    Thanks Robert.

    The entire code feels like a weak hack to me the way I'm doing it.
    For example. Because I'm switching from polygons mode to points mode to get around the rotating shared edges too many times problem. That means that if the user had points already selected. They would get trashed. Unless I saved them, then restored them after the rotation is done.

    All I'm trying to do is rotate the selected polygons by an amount.
    Like when we're in polygons mode, and we type in a value into the rotation gizmos under the Editor Window. And I'm sure I'm over complicating things and approaching it wrong.

    Since the values in these gizmos always return to zero after changing them. I'm guessing that means that Maxon is using a temporary matrix to rotate them. Then deleting it.
    Otherwise I'd assume the values in the gizmos would stay where we put them.
    I'm not sure if that is correct...Just how I'm guessing it works. And that is the reason why I'm using virtual matrix's in my code.
    I have no ideal if this is correct or not.

    I'd really like to know how Maxon is doing it.
    Because the way I'm doing it seems overly complicated. And wrong.

    -ScottA


Log in to reply