Hello once again;
I have expanded some scripted automatisms for CTrack, CCurve and CKey from pure float values to other types, and it turned out to be somewhat irritating. The class CKey
provides two different methods to set/get values: SetValue
and SetGeData
. Documentation tells me that the former is for floats, the latter for "any", as Python does not have a GeData
class.
Although... the doc now gets cryptic. SetGeData
precisely says it sets "the data" of the key - not "the value". The C++ doc tells me to use SetValue
only for floats and "Use GetParameter()
to read non-real values" (with an example). SetGeData
in the C++ docs says "Sets the data of the key" as well; it's so consistent that I begin to suspect "value" and "data" of a key are actually two different things.
As SetValue
works fine for floats, I try SetGeData
for a DTYPE_LONG
track anyway (PRIM_CUBE_SUBX
). Doesn't work as expected. In the Timeline, the result is always 0, and the animation widget in the Attribute Manager shows up as a yellow circle as if that frame doesn't have any key.
On the other hand, for a DTYPE_BOOL
track, SetGeData
works fine.
After some searching, I get an answer on this very forum: I must take the track category into account. CTRACK_CATEGORY_VALUE
requires GetValue
, and CTRACK_CATEGORY_DATA
needs GetGeData
. And CTRACK_CATEGORY_PLUGIN
, well, I still have not the slightest idea, but I suppose GetParameter
may be the thing here?
I'm doing the following test script:
(this works only for a selected cube primitive and creates keys for the Fillet boolean parameter and the X segments value)
import c4d
from c4d import gui
def SetKey (currentTime, obj, parameterDesc, value):
track = obj.FindCTrack(parameterDesc)
print ("To set:", value, type(value))
if track == None:
# create track for this parameter
track = c4d.CTrack(obj, parameterDesc)
obj.InsertTrackSorted(track)
curve = track.GetCurve() # always present
cat = track.GetTrackCategory()
if cat == c4d.CTRACK_CATEGORY_PLUGIN:
print ("Track is Plugin")
return
elif cat == c4d.CTRACK_CATEGORY_VALUE:
print ("Track is Value")
elif cat == c4d.CTRACK_CATEGORY_DATA:
print ("Track is Data")
else:
print ("Track is unknown")
return
def SetKey_Value(key, value):
if cat == c4d.CTRACK_CATEGORY_VALUE:
origData = key.GetValue()
print ("Orig:", origData, type(origData))
key.SetValue(curve, value)
postData = key.GetValue()
print ("Post:", postData, type(postData))
elif cat == c4d.CTRACK_CATEGORY_DATA:
origData = key.GetGeData()
print ("Orig:", origData, type(origData))
key.SetGeData(curve, value)
postData = key.GetGeData()
print ("Post:", postData, type(postData))
currentKey = curve.FindKey(currentTime)
if currentKey:
key = currentKey['key'] # key reference to change
doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, key)
SetKey_Value(key, value)
curve.SetKeyDirty()
# Key attributes found under C++ API doc: ckvalue.h File Reference
else:
defkey, dub = doc.GetDefaultKey() # new key to insert
key = defkey.GetClone() # needed?
SetKey_Value(key, value)
key.SetTime(curve, currentTime)
# key.SetInterpolation(curve, c4d.CINTERPOLATION_SPLINE)
curve.InsertKey(key, True)
doc.AddUndo(c4d.UNDOTYPE_NEW, key)
def main():
if op == None : return
descSubX = c4d.DescID(c4d.DescLevel(c4d.PRIM_CUBE_SUBX, c4d.DTYPE_LONG, 0))
descFillet = c4d.DescID(c4d.DescLevel(c4d.PRIM_CUBE_DOFILLET, c4d.DTYPE_BOOL, 0))
doc.StartUndo()
val = op[c4d.PRIM_CUBE_SUBX]
SetKey (doc.GetTime(), op, descSubX, val)
val = op[c4d.PRIM_CUBE_DOFILLET]
SetKey (doc.GetTime(), op, descFillet, val)
doc.EndUndo()
c4d.EventAdd()
if __name__=='__main__':
main()
This works fine, and tells me in the console output that actually a DTYPE_LONG
DescID has created a track of category CTRACK_CATEGORY_VALUE
, and my integer value that I'm trying to pass to the key is converted into a float
. And the DTYPE_BOOL
DescID has created a track of category CTRACK_CATEGORY_DATA
, and my Boolean value that I'm trying to pass to the key is converted into an int
.
While I now have a function that seems to work for two track categories (I haven't even started on the "plugin" category), this is not what I would have expected from the documentation. I later discover the track category thing in the C++ CKey manual as well, but there it only again mentions float
values.
Questions:
- Is my solution for
DTYPE_LONG
even correct? It works, but the solution is pretty much not what I gather from the API docs. - So a
CKey
stores an int as float, and a bool as int, and... what's the mapping here? - Are the
Set/GetValueLeft/Right
functions similarly dispositioned? The Python docs say "Just for keys with float values." but right now I suspect that they will also work forDTYPE_LONG
tracks as the according keys seem to work with floats internally. (Also, the Timeline shows me tangents.) - Checking the track categories, I find that a PLA track (as the C++ CKey API doc shows as an example) is a Plugin track. So, am I right with my theory that Plugin tracks need
GetParameter
, just like Value tracks needGetValue
and Data tracks needGetGeData
? The doc says "UseGetParameter()
to read non-real values." in the same place, but that is not takingGetGeData
in account at all? (I'm not even going to mention thatGetParameter
also usesGeData
...)