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
[URL-REMOVED]* GetHelperAxis(void)_<_h4_>_The helper axis for the current multi selection.<_<_h5_>_h5_<_h5_>_rn
>BaseObject
[URL-REMOVED]*
>
>> The axis object. The document owns the pointed object.
>>
>>
>Adios,
Cactus Dan
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
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