SOLVED Creating a Keyframe similar to Ctrl-Clicking the Curve

Hello,
When in the Timeline, if I hold Ctrl/Cmd and click on the curve, Cinema 4D adds a keyframe that matches the contour of the curve perfectly.
Curve2.jpg

The tangents of the new key and neighboring keys are properly weighted.

When I try to do this with the code below, I get a keyframe whose angles do not match the curve and where the Left & Right times of the neighboring keys are not adjusted.

keyDict = keyCurve.AddKey(keyTime)
keyIndex = keyDict["nidx"]
key = keyDict["key"]
key.SetValue(keyCurve,value)
key[c4d.ID_CKEY_PRESET] = c4d.ID_CKEY_PRESET_NEWAUTOWEIGHTED

Result:
10972325-56f5-40c8-b50f-b32d3025c6bd-image.png

The tangents of the key seem to be weighted properly using ID_CKEY_PRESET_NEWAUTOWEIGHTED, but how can I get the angle of the tangents to match the curve and the Left & Right Time of the neighboring keys to adjust so the curve's contour doesn't change as in the Ctrl-Click key creation scenario?

Thank you!

Hi @blastframe,

thank you for reaching out to us. One built in solution you could use is CCurve.AddKeyAdaptTangent() instead of CCurve.AddKey(). It will allow you to insert a smoothly interpolated key between two other keys and the result will look something like the attached image. I also attached a modified github ctrack_create_keys_r13.py example at the end. You only have to read the snip parts, everything else is unchanged.

2798b905-ec91-4bc1-8363-ad0d65577044-image.png

I hope this helps, if not, I would have to ask you to restate your goals.

Cheers,
Ferdinand

"""
Copyright: MAXON Computer GmbH
Author: Manuel MAGALHAES
Description:
    - Creates position Y tracks.
    - Adds two keyframes.
    - Sets their value and interpolation.
Class/method highlighted:
    - CKey.SetInterpolation()
    - CKey.SetKeyDefault()
Compatible:
    - Win / Mac
    - R13, R14 R15, R16, R17, R18, R19, R20, R21, S22, R23
"""

import c4d


def CreateKey(curve, time, value, interpolation):
    """
    Creates a Key on the given curve at the given time with a given value and with the given interpolation.
    :param curve: The Curve where to add the keyframe.
    :type curve: c4d.CCurve
    :param time: The time of the keyframe.
    :type time: c4d.BaseTime
    :param value: The value of the keyframe.
    :type value: float
    :param interpolation: The interpolation of the key along the curve.
    :type interpolation: c4d.CINTERPOLATION_XXX
    :return: They key and the index of the key in the CCurve.
    :rtype: tuple(c4d.Ckey, int)
    :raise MemoryError: If for some reason, it was not possible to create the key.
    """
    # Adds the key
    keyDict = curve.AddKey(time)

    # Checks if the key have been added
    if keyDict is None:
        raise MemoryError("Failed to create a key")

    # Retrieves the inserted key
    key = keyDict["key"]
    keyIndex = keyDict["nidx"]

    # Sets the value of the key
    key.SetValue(curve, value)

    # Mandatory: Fill the key with default settings
    curve.SetKeyDefault(doc, keyIndex)

    # Changes it's interpolation
    key.SetInterpolation(curve, interpolation)

    return key, keyIndex


def main():
    # Creates the object in memory
    obj = c4d.BaseObject(c4d.Ocube)

    # Creates the track in memory. Defined by it's DescID
    trackY = c4d.CTrack(obj, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0),
                                        c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0)))

    # Gets curves for the track
    curveY = trackY.GetCurve()

    # Creates a key in the Y curve with value 0 at 0 frame with a spline interpolation
    keyA, keyAIndex = CreateKey(curveY, c4d.BaseTime(0), value=0, interpolation=c4d.CINTERPOLATION_SPLINE)

    # Retrieves time at frame 10
    keyBTime = c4d.BaseTime(10, doc.GetFps())

    # Creates another key in the Y curve with value 100 at 10 frame with a spline interpolation
    keyB, keyBIndex = CreateKey(curveY, keyBTime, value=100, interpolation=c4d.CINTERPOLATION_SPLINE)
    
    # --- snip ---
    
    # Add key in between the keys A and B.
    bt = c4d.BaseTime(5, doc.GetFps())
    curveY.AddKeyAdaptTangent(bt, True, True)
    
    # --- snip ---

    # Inserts the track containing the Y curve to the object
    obj.InsertTrackSorted(trackY)

    # Inserts the object in document
    doc.InsertObject(obj)

    # Pushes an update event to Cinema 4D
    c4d.EventAdd()


# Execute main()
if __name__ == '__main__':
    main()

Hi @blastframe,

thank you for reaching out to us. One built in solution you could use is CCurve.AddKeyAdaptTangent() instead of CCurve.AddKey(). It will allow you to insert a smoothly interpolated key between two other keys and the result will look something like the attached image. I also attached a modified github ctrack_create_keys_r13.py example at the end. You only have to read the snip parts, everything else is unchanged.

2798b905-ec91-4bc1-8363-ad0d65577044-image.png

I hope this helps, if not, I would have to ask you to restate your goals.

Cheers,
Ferdinand

"""
Copyright: MAXON Computer GmbH
Author: Manuel MAGALHAES
Description:
    - Creates position Y tracks.
    - Adds two keyframes.
    - Sets their value and interpolation.
Class/method highlighted:
    - CKey.SetInterpolation()
    - CKey.SetKeyDefault()
Compatible:
    - Win / Mac
    - R13, R14 R15, R16, R17, R18, R19, R20, R21, S22, R23
"""

import c4d


def CreateKey(curve, time, value, interpolation):
    """
    Creates a Key on the given curve at the given time with a given value and with the given interpolation.
    :param curve: The Curve where to add the keyframe.
    :type curve: c4d.CCurve
    :param time: The time of the keyframe.
    :type time: c4d.BaseTime
    :param value: The value of the keyframe.
    :type value: float
    :param interpolation: The interpolation of the key along the curve.
    :type interpolation: c4d.CINTERPOLATION_XXX
    :return: They key and the index of the key in the CCurve.
    :rtype: tuple(c4d.Ckey, int)
    :raise MemoryError: If for some reason, it was not possible to create the key.
    """
    # Adds the key
    keyDict = curve.AddKey(time)

    # Checks if the key have been added
    if keyDict is None:
        raise MemoryError("Failed to create a key")

    # Retrieves the inserted key
    key = keyDict["key"]
    keyIndex = keyDict["nidx"]

    # Sets the value of the key
    key.SetValue(curve, value)

    # Mandatory: Fill the key with default settings
    curve.SetKeyDefault(doc, keyIndex)

    # Changes it's interpolation
    key.SetInterpolation(curve, interpolation)

    return key, keyIndex


def main():
    # Creates the object in memory
    obj = c4d.BaseObject(c4d.Ocube)

    # Creates the track in memory. Defined by it's DescID
    trackY = c4d.CTrack(obj, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0),
                                        c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0)))

    # Gets curves for the track
    curveY = trackY.GetCurve()

    # Creates a key in the Y curve with value 0 at 0 frame with a spline interpolation
    keyA, keyAIndex = CreateKey(curveY, c4d.BaseTime(0), value=0, interpolation=c4d.CINTERPOLATION_SPLINE)

    # Retrieves time at frame 10
    keyBTime = c4d.BaseTime(10, doc.GetFps())

    # Creates another key in the Y curve with value 100 at 10 frame with a spline interpolation
    keyB, keyBIndex = CreateKey(curveY, keyBTime, value=100, interpolation=c4d.CINTERPOLATION_SPLINE)
    
    # --- snip ---
    
    # Add key in between the keys A and B.
    bt = c4d.BaseTime(5, doc.GetFps())
    curveY.AddKeyAdaptTangent(bt, True, True)
    
    # --- snip ---

    # Inserts the track containing the Y curve to the object
    obj.InsertTrackSorted(trackY)

    # Inserts the object in document
    doc.InsertObject(obj)

    # Pushes an update event to Cinema 4D
    c4d.EventAdd()


# Execute main()
if __name__ == '__main__':
    main()

@ferdinand That was exactly what I was seeking, thank you, Ferdinand! 😄