Hello @yaya,

thank you for reaching out to us. I must first point out that it is better to provide explicit code for such questions, as we then have to write less boiler plate code when answering, and also assume less.

A point in the local coordinate system of an object (or anything else as for example a vertex or polygon) is just that point. So, the point `(0, 10, 0)`

in local coordinates for the object `cube`

, is just that, `(0, 10, 0)`

. To convert such local point back to global coordinates, one must multiply it with the transform of the coordinate system it is associated with, e.g., the global matrix of an object. I have provided below a code example which goes over some basics for your specific case.

Such questions are all more less just vector math/linear algebra questions which are formally out of scope of support. We have the Matrix Manal in Python where I went over some stuff, but we cannot explain all the math in our APIs. We recognize that there are more important mathematical concepts where it is in our interest to explain them, but for now C++ receives less attention, as we assume users there to be more technically oriented than our Python users. I must point out that I cannot lead you step by step to following linear algebra questions.

I know that is likely not what you wanted to hear, but we must throttle user requests in some form. Questions about our APIs are of course still welcome.

Thank you for your understanding,

Ferdinand

The result:

The code:

```
/*Simple example for transforming points in and out of coordinate systems.
The example will look for the first object in the scene, place cube between the position of that
object and its first child, then compute two points in relation to these objects, and finally
insert two speheres, representing these points.
As discussed in:
https://plugincafe.maxon.net/topic/13895/
*/
#include "c4d.h"
#include "c4d_symbols.h"
#include "main.h"
#include "maxon/lib_math.h"
const maxon::Int32 PID_1059349 = 1059349;
// A boiler-plate CommandData plugin, it just runs the example.
class PC1059349: public CommandData
{
INSTANCEOF(PC1059349, CommandData)
public:
virtual Bool Execute(BaseDocument* doc, GeDialog* parentManager);
};
/// Provides the example.
Bool PC1059349::Execute(BaseDocument* doc, GeDialog* parentManager)
{
if (doc == nullptr)
return false;
// Get the first object, its first child, and allocate a cube object.
BaseObject* const parent = doc->GetFirstObject();
if (parent == nullptr)
return true;
BaseObject* const child = parent->GetDown();
if (child == nullptr)
return true;
BaseObject* cube = BaseObject::Alloc(Ocube);
if (child == nullptr)
return true;
// Insert the cube as the first child under #parent and set its global position to be half way
// between #parent and #child.
cube->InsertUnder(parent);
Matrix globalCubeTransform;
// The offset in global space of the cube is being set as the t=.5 linear interpolation between
// the global offset of the parent and the child.
globalCubeTransform.off = maxon::Blend(parent->GetMg().off, child->GetMg().off, 0.5);
cube->SetMg(globalCubeTransform);
// Print out the local and global transform of #cube.
// This is the transform of #cube in relation to parent.
ApplicationOutput("Cube.GetMl(): @", cube->GetMl());
// This is the transform of #cube in relation to the world coordinate system. It is equal to the
// product of the local transform of #parent and #cube. One should use global transform when
// ever possible, as operating in local coordinate systems can complicate things.
ApplicationOutput("Cube.GetMg(): @", cube->GetMg());
// Constructing the point (0, 200, 0) in the local coordinate system of #cube. There is nothing
// else to it, the purpose of a local coordinate system is that it is in relation to some 'thing'.
const Vector pLocal(0, 200, 0);
// Translate that point into global coordinates: For that, the point must be transformed by the
// coordinate system (i.e., transform/matrix) it is living in, i.e., the global transform of
// #cube in this case. Note that matrix multiplication in the C++ API forces one to use the
// Matrix first form for matrix-vector transforms. It must be M * p and cannot be p * M.
const Vector pGlobal = cube->GetMg() * pLocal;
// Print out both coordinates.
ApplicationOutput("pLocal: @", pLocal);
ApplicationOutput("pGlobal: @", pGlobal);
// Insert that point as a red sphere into the document.
BaseObject* sphereRed = BaseObject::Alloc(Osphere);
if (sphereRed == nullptr)
return true;
// Turn on the display color and set it to red.
sphereRed->SetParameter(DescID(ID_BASEOBJECT_USECOLOR), true, DESCFLAGS_SET::NONE);
sphereRed->SetParameter(DescID(ID_BASEOBJECT_COLOR), Vector(1, 0, 0), DESCFLAGS_SET::NONE);
sphereRed->InsertUnder(cube);
// Even though a global coordinate is being set for #sphereRed, the object can be still parented
// to another object, Cinema 4D will resolve the details. This will result in #sphereRed being
// located directly 'above' #cube (i.e., (0, 200, 0) in local coordinates of #cube).
Matrix mgPointRed;
mgPointRed.off = pGlobal;
sphereRed->SetMg(mgPointRed);
// Now we are going to convert the global point #pGlobal to a local coordinate system. We choose
// #child for that, i.e., express #pGlobal in the coordinate system of #child.
// This can be done by multiplying the point by the inverse global transform of the object we want
// to convert the global point to. The ~ operator returns the inverse of a matrix/transform.
Vector pLocalChild = ~child->GetMg() * pGlobal;
// But when we now would insert another sphere with #pLocalChild as the offset of its local
// transform, and with this sphere was being parented to #child, this would result in the red
// sphere and the other sphere overlapping, as #pGlobal and #pLocalChild would then be same point,
// just expressed in different coordinate systems.
// Instead, we are going to first compute the global point #-pLocal, i.e., a point which will fit
// a sphere directly below the cube, and then convert that point into the local coordinate system
// of child. Note that matrix multiplication is not commutative, i.e., A * B != B * A, so the
// order "~child->GetMg() * cube->GetMg()" matters here.
pLocalChild = ~child->GetMg() * cube->GetMg() * -pLocal;
// Insert a sphere object under #child for the second point.
BaseObject* sphereBlue = BaseObject::Alloc(Osphere);
if (sphereBlue == nullptr)
return true;
sphereBlue->SetParameter(DescID(ID_BASEOBJECT_USECOLOR), true, DESCFLAGS_SET::NONE);
sphereBlue->SetParameter(DescID(ID_BASEOBJECT_COLOR), Vector(0, 0, 1), DESCFLAGS_SET::NONE);
sphereBlue->InsertUnder(child);
// Set the local transform of #sphereBlue with SetMl() to pLocalChild.
Matrix mlPointBlue;
mlPointBlue.off = pLocalChild;
sphereBlue->SetMl(mlPointBlue);
// Let Cinema 4D catch up to the document modifications which have been made.
EventAdd();
return true;
}
// Register the plugin.
PC1059349* g_cmd;
Bool RegisterPC1059349()
{
g_cmd = NewObjClear(PC1059349);
return RegisterCommandPlugin(PID_1059349, "pc1059349"_s, 0, nullptr, ""_s, g_cmd);
}
```