Cool Frame C4D Script

September 2022, MIT License.

The script is completely free, you can correct any mistakes in the code or optimize it, but cannot re-sell it.

Installation

  1. Download the zip archive and extract it.
  2. Place the coolframe.c4d  file in a drive/folder that’s not gonna move in the years to come. (eg: E:/Assets/Cinema4d/PythonScripts/coolframe.c4d)
  3. Open Cinema4d and go to Extensions > User Scripts > Script Folder
  4. Place the coolframe.py  in that folder.
  5. Close and Re-Open Cinema4d, and go to Extensions > Script Manager, select the coolframe.py script.
  6. In the code, replace the directory by where you put coolframe.c4d in step 2.
  7. Before exiting the script editor, save the changes, and voilà!

Limitations

It is my first-ever script, so it might be quite buggy. To fully benefit of this script, you may need to add some Xpresso nodes to make more relations here and there.

Make sure that, in the object manager, all of the parent frames are calculated before their respective children, to ensure a smooth preview.

I feel like it could be useful for a lot of motion designers, so I’m just sharing a tool that I use all the time, for free.

(And I loved making that silly promo video) 

Example Files

Quick view of the Python tag code for my fellow nerds 🤓

import c4d

def main():
    obj = op.GetObject()
    bc = c4d.BaseContainer()
    obj.AddEventNotification(op, c4d.NOTIFY_EVENT_MESSAGE, 0, bc)
    parent = obj[c4d.ID_USERDATA,14] #Parenting : Link to Parent frame
    mix_x = obj[c4d.ID_USERDATA,15] #Parenting : Interpolation of X Position between the parent edges (in %)
    mix_y = obj[c4d.ID_USERDATA,16] #Parenting : Interpolation of Y Position between the parent edges (in %)
    margin_w = obj[c4d.ID_USERDATA,20] #Parenting : Margin for width
    margin_h = obj[c4d.ID_USERDATA,22] #Parenting : Margin for height
    mult_w = obj[c4d.ID_USERDATA,28] #Parenting : Multiplier of width
    mult_h = obj[c4d.ID_USERDATA,31] #Parenting : Multiplier of height
    radbool = obj[c4d.ID_USERDATA,23] #Parenting : Checkbox for radius
    disap_tresh = obj[c4d.ID_USERDATA,33] #Disappearing Treshold
    align_x = c4d.utils.RangeMap(obj[c4d.ID_USERDATA,34],0,2,1,-1,False) #Converts the left, center, right into usable data
    align_y = c4d.utils.RangeMap(obj[c4d.ID_USERDATA,35],0,2,-1,1,False) #Converts the top, center, bottom into usable data
    color_bool = obj[c4d.ID_USERDATA,37] #Use custom color?
    icon_bool = obj[c4d.ID_USERDATA,39] #Use it for icon?
    color_custom = obj[c4d.ID_USERDATA,38] #Custom color
    default_color = c4d.Vector(0.29, 0.529, 1)



    if obj[c4d.ID_USERDATA,19] == False:
        width = obj[c4d.ID_USERDATA,3]
    else:
        width = parent[c4d.PRIM_RECTANGLE_WIDTH]*mult_w-(margin_w*2)
    obj[c4d.PRIM_RECTANGLE_WIDTH] = width
    minw = width*0.5

    if obj[c4d.ID_USERDATA,21] == False:
        height = obj[c4d.ID_USERDATA,4]
    else:
        height = parent[c4d.PRIM_RECTANGLE_HEIGHT]*mult_h-(margin_h*2)
    obj[c4d.PRIM_RECTANGLE_HEIGHT] = height
    minh = height*0.5


    small = min(minh,minw)

    rounding = obj[c4d.ID_USERDATA,9]
    obj[c4d.PRIM_RECTANGLE_ROUNDING] = rounding


    if radbool == True:
        radius = parent[c4d.PRIM_RECTANGLE_RADIUS] - obj[c4d.ID_USERDATA,24]
    else:
        radius = obj[c4d.ID_USERDATA,5]

    if small > radius:
        obj[c4d.PRIM_RECTANGLE_RADIUS] = radius
    else:
        obj[c4d.PRIM_RECTANGLE_RADIUS] = small

    #Custom color
    if color_bool == False:
        obj[c4d.ID_BASEOBJECT_USECOLOR] = 0 #Material
    else:
        obj[c4d.ID_BASEOBJECT_USECOLOR] = 2 #Custom color
        obj[c4d.ID_BASEOBJECT_COLOR] = color_custom

    if icon_bool == True:
        obj[c4d.ID_BASELIST_ICON_COLOR] = color_custom
    else:
        obj[c4d.ID_BASELIST_ICON_COLOR] = default_color





#Disappearing Treshold

    if height < disap_tresh:
        obj[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = 0
    elif width < disap_tresh:
        obj[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = 0
    else:
        obj[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = 1




    xoffset = obj[c4d.ID_USERDATA,7]
    yoffset = obj[c4d.ID_USERDATA,8]




    if parent == None:
        parent_x = 0
        parent_y = 0
        pw = 0
        ph = 0
        rel_x_pos = (width*0.5)*align_x  + xoffset
        rel_y_pos = (height*0.5)*align_y  + yoffset
    else:
        if parent[c4d.ID_USERDATA,26] == None:
            parent_x = c4d.BaseObject.GetRelPos(parent)[0]
            parent_y = c4d.BaseObject.GetRelPos(parent)[1]
            pw = parent[c4d.PRIM_RECTANGLE_WIDTH]
            ph = parent[c4d.PRIM_RECTANGLE_HEIGHT]
        else:
            parent_x = c4d.BaseObject.GetRelPos(parent[c4d.ID_USERDATA,26])[0]
            parent_y = c4d.BaseObject.GetRelPos(parent[c4d.ID_USERDATA,26])[1]
            pw = parent[c4d.PRIM_RECTANGLE_WIDTH]
            ph = parent[c4d.PRIM_RECTANGLE_HEIGHT]

        paw = c4d.utils.RangeMap(mix_x, 0, 1, pw*-0.5, pw*0.5, False)
        pah = c4d.utils.RangeMap(mix_y, 0, 1, ph*0.5, -ph*0.5, False)


        if obj[c4d.ID_USERDATA,11] == True: #If overflow X is active:
            rel_x_pos = parent_x + paw + (width*0.5)*align_x  + xoffset
        else: #if overflow X is not active:
            paw_of = c4d.utils.RangeMap(mix_x, 0, 1, (pw*-0.5 + width*0.5), (pw*0.5-width*0.5), False)
            rel_x_pos = parent_x + paw_of + xoffset

        if obj[c4d.ID_USERDATA,12] == True: #If overflow Y is active:
            rel_y_pos = parent_y + pah + (height*0.5)*align_y  + yoffset
        else: #if overflow Y is not active:
            pah_of = c4d.utils.RangeMap(mix_y, 0, 1, (ph*-0.5 + height*0.5), (ph*0.5-height*0.5), False)
            rel_y_pos = parent_y + pah_of + yoffset


    ext = obj[c4d.ID_USERDATA,26]


    if ext == None:
        obj[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_X] = rel_x_pos
        obj[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Y] = rel_y_pos
    else:
        obj[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_X] = 0
        obj[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Y] = 0
        ext[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_X] = rel_x_pos
        ext[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Y] = rel_y_pos
        #Custom color
        if color_bool == False:
            ext[c4d.ID_BASEOBJECT_USECOLOR] = 0 #Material
        else:
            ext[c4d.ID_BASEOBJECT_USECOLOR] = 2 #Custom color
            ext[c4d.ID_BASEOBJECT_COLOR] = color_custom

        if icon_bool == True:
            ext[c4d.ID_BASELIST_ICON_COLORIZE_MODE] = 1
            ext[c4d.ID_BASELIST_ICON_COLOR] = color_custom
        else:
            ext[c4d.ID_BASELIST_ICON_COLORIZE_MODE] = 0


#Creating the extruded Object

def message(msg_type, data):
    obj = op.GetObject()
    if msg_type == c4d.MSG_NOTIFY_EVENT:
        event_data = data['event_data']
        if event_data['msg_id'] == c4d.MSG_DESCRIPTION_COMMAND:
            desc_id = event_data['msg_data']['id']
            if desc_id[1].id == 25:
                extrude  = c4d.BaseObject(5116) #Create the extrude
                doc.InsertObject(extrude) #Insert the extrude into the scene
                obj.InsertUnder(extrude) #Insert Frame under extrude
                extrude.SetName(obj.GetName()+'_ext') #Rename it w/ the original frame name
                obj[c4d.ID_USERDATA,26] = extrude #Place Extrude ref in Frame
                extrude[c4d.EXTRUDEOBJECT_EXTRUSIONOFFSET] = 1 #Set Extrusion to 1
                c4d.CallCommand(600000152) #Select Extrude
                c4d.CallCommand(100004802) #Unfold
                c4d.CallCommand(600000140) #select Frame