SOLVED CKey Auto Tangents

When I try to use GetTimeRight() or GetValueRight() to get a key frame with Preset set to CKEYPRESET::NEWAUTO, the returned value is not the correct value (0 or some large value). How can I get the tangent of the curve, or Tell me how to calculate them by myself.

Thinks,
AiMiDi

Hello @aimidi,

so, I did revisit your problem and CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right do indeed not work with key presets. They will always return the non-adjusted values for the key, i.e., what equates to the preset CUSTOM. It is a bit a matter of opinion if this could be considered a bug or not, since there are some indicators in our code that this has been implemented intentionally in this way for performance reasons.

If you want to retrieve the true tangents for a key, i.e., the tangents which take the presets into account, you must use CCurve::GetTangents where the documentation already states that they do this.

Currently there is no warning in the docs that CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right will behave in this way. I will reach out to the developers if we want to change this behavior. If we do not, we will update the documentation of CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right in an upcoming release to reflect that this will always return the underlying custom tangents (which are sort of invisibly always there).

Below you will find a code example in C++ and the matching output which should be what you are after.

Sorry for the longwinded thread and cheers,
Ferdinand

The output:
c565fc4d-8c1b-488d-8288-e8a508af90a5-image.png

The code:

// For https://plugincafe.maxon.net/topic/13344

#include "c4d.h"
#include "c4d_symbols.h"

static maxon::Result<void> pc13344(BaseDocument* doc)
{
    iferr_scope;

    BaseObject* op = doc->GetFirstObject();
    if (op == nullptr) {
        ApplicationOutput("Please insert at least one object into the scene.");
        return maxon::OK;
    }

    CTrack* track = op->GetFirstCTrack();
    if (track == nullptr) {
        ApplicationOutput("The first object in the scene has no animation track.");
        return maxon::OK;
    }

    CCurve* curve = track->GetCurve();
    if (curve == nullptr) {
        ApplicationOutput("The first object's animation track has no curve/keys.");
        return maxon::OK;
    }

    for (int index = 0; index < curve->GetKeyCount(); index++)
    {
        CKey* key = curve->GetKey(index);
        CKEYPRESET preset = key->GetKeyPreset();

        maxon::Float keyTleftX = key->GetTimeLeft().Get(), 
                     keyTleftY = key->GetValueLeft(), 
                     keyTRightX = key->GetTimeRight().Get(), 
                     keyTRightY = key->GetValueRight();

        maxon::Float64 curveTleftX = 0., curveTleftY = 0., curveTRightX = 0., curveTRightY = 0.;
        curve->GetTangents(index, &curveTleftX, &curveTRightX, &curveTleftY, &curveTRightY);

        ApplicationOutput("index: @, mode: @, keyLeftTangent: (@, @), keyRightTangent: (@, @), 
                           curveLeftTangent: (@, @), curveRightTangent: (@, @)",
                          index, preset, 
                          keyTleftX, keyTleftY, 
                          keyTRightX, keyTRightY, 
                          curveTleftX, curveTleftY, 
                          curveTRightX, curveTRightY);
    }
    return maxon::OK;
}

Hello @AiMiDi,

thank you for reaching out to us. Please remember to add tags to your postings, so that we can answer them better.

With S24, there is no CKEYPRESET::NEWAUTO anymore, its has been depreciated. See the S24 change list for ge_prepass.h.

Aside from that, I cannot reproduce the reported behavior of the preset NEWAUTO, or its S24 equivalent AUTO_OVERSHOOT, not working in conjunction with retrieving the right tangent. See script attached at the end. The only thing we could think of, is that you might have created that key yourself and it has not been updated yet.

To get any further help, I would have to ask you to provide more details and/or post code.

Cheers,
Ferdinand

The code to test with:

import c4d

if c4d.GetC4DVersion() >= 24000:
    CKEY_PRESET_SYMBOLS = {
        1: "AUTO_CLAMP",
        2: "AUTO_OVERSHOOT",
        3: "FIXED_OVERSHOOTWEIGHTED",
        4: "CUSTOM",
        5: "AUTO_OVERSHOOTWEIGHTED",
    }
else:
    CKEY_PRESET_SYMBOLS = {
        1: "CLASSICAUTO",
        2: "NEWAUTO",
        3: "NEWAUTOWEIGHTED",
        4: "CUSTOM",
    }
    
CKEY_DATA_MESSAGE = "id: {}, mode: {}, left: {}, right: {}"


def main():
    """
    """
    def getKeyData(index, curve):
        """
        """
        key = curve.GetKey(index)
        preset = key[c4d.ID_CKEY_PRESET]
        mode = CKEY_PRESET_SYMBOLS[preset]
        left = (key.GetTimeLeft().Get(), key.GetValueLeft())
        right = (key.GetTimeRight().Get(), key.GetValueRight())
        return CKEY_DATA_MESSAGE.format(index, mode, left, right)

    if not op:
        return
    firstTrack = op.GetFirstCTrack()
    if not firstTrack:
        return
    curve = firstTrack.GetCurve()
    if curve is None:
        return

    for index in range(curve.GetKeyCount()):
        data = getKeyData(index, curve)
        print(data)


if __name__ == '__main__':
    main()

The curve shown uses keys for all interpolation presets.
R23:
353598ec-c5ea-4324-bf6c-d9edac376c3f-image.png
S24:
8509742f-a466-40cc-844a-53e878b62116-image.png

Hi @ferdinand,

thank you so much for replying to me.I read the code you provided, and I think I didn't express it clearly.353598ec-c5ea-4324-bf6c-d9edac376c3f-image.png
For the key frame I circled in this picture, its tangent should be horizontal(Used Remove Overshooting), so its GetTimeRight() or GetValueRight() value should be 0 instead of the number obtained in the picture, can you understand what I said.

Thinks,
AiMiDi

Hi @AiMiDi,

ah, I now do understand, you are right, these outputs look indeed to be wrong, I totally overlooked that. I will have to take a closer look if this is a bug, or you and I simply misunderstand the purpose of these methods. As I would also expect them to effectively provide a tangent vector relative to the key vertex.

Cheers,
Ferdinand

Hello @aimidi,

so, I did revisit your problem and CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right do indeed not work with key presets. They will always return the non-adjusted values for the key, i.e., what equates to the preset CUSTOM. It is a bit a matter of opinion if this could be considered a bug or not, since there are some indicators in our code that this has been implemented intentionally in this way for performance reasons.

If you want to retrieve the true tangents for a key, i.e., the tangents which take the presets into account, you must use CCurve::GetTangents where the documentation already states that they do this.

Currently there is no warning in the docs that CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right will behave in this way. I will reach out to the developers if we want to change this behavior. If we do not, we will update the documentation of CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right in an upcoming release to reflect that this will always return the underlying custom tangents (which are sort of invisibly always there).

Below you will find a code example in C++ and the matching output which should be what you are after.

Sorry for the longwinded thread and cheers,
Ferdinand

The output:
c565fc4d-8c1b-488d-8288-e8a508af90a5-image.png

The code:

// For https://plugincafe.maxon.net/topic/13344

#include "c4d.h"
#include "c4d_symbols.h"

static maxon::Result<void> pc13344(BaseDocument* doc)
{
    iferr_scope;

    BaseObject* op = doc->GetFirstObject();
    if (op == nullptr) {
        ApplicationOutput("Please insert at least one object into the scene.");
        return maxon::OK;
    }

    CTrack* track = op->GetFirstCTrack();
    if (track == nullptr) {
        ApplicationOutput("The first object in the scene has no animation track.");
        return maxon::OK;
    }

    CCurve* curve = track->GetCurve();
    if (curve == nullptr) {
        ApplicationOutput("The first object's animation track has no curve/keys.");
        return maxon::OK;
    }

    for (int index = 0; index < curve->GetKeyCount(); index++)
    {
        CKey* key = curve->GetKey(index);
        CKEYPRESET preset = key->GetKeyPreset();

        maxon::Float keyTleftX = key->GetTimeLeft().Get(), 
                     keyTleftY = key->GetValueLeft(), 
                     keyTRightX = key->GetTimeRight().Get(), 
                     keyTRightY = key->GetValueRight();

        maxon::Float64 curveTleftX = 0., curveTleftY = 0., curveTRightX = 0., curveTRightY = 0.;
        curve->GetTangents(index, &curveTleftX, &curveTRightX, &curveTleftY, &curveTRightY);

        ApplicationOutput("index: @, mode: @, keyLeftTangent: (@, @), keyRightTangent: (@, @), 
                           curveLeftTangent: (@, @), curveRightTangent: (@, @)",
                          index, preset, 
                          keyTleftX, keyTleftY, 
                          keyTRightX, keyTRightY, 
                          curveTleftX, curveTleftY, 
                          curveTRightX, curveTRightY);
    }
    return maxon::OK;
}

Hi @ferdinand,
Thank you very much for your reply. Your reply solved my problem very well, thank you.

thanks again,
AiMiDi