SOLVED UserData

Hello everyone, I'm new here and start learning some development tips inside c4d.
I need help with file attached and UserData part. What I want to do with this plane object is under TestSettings / Height slider - I would like to scale plane object proportionally in 2 relevant axis, with slider of course... ofr exaple with 100 as min and 300 as max values.
Hope someone is interested to help me and it's not too difficult !

Thanks so much.

1f6e3d91-ae02-4c5e-8a21-2a7f347388d3-image.png
56769c74-b0f2-42da-bfec-cdeabd4df401-height_user_data.c4d

Hello @bokibo,

welcome to the Plugin Café and the Cinema 4D development community and thank you for reaching out to us. I would propose that you look at our Forum Guidelines, as they explain the form of a posting and forum features. I have marked your posting as a question for you in this case, updated your tags a bit to better reflect what this is about, and also moved the topic into the correct forum.

About your question: Although you do not indicate anything like that, I would like to clarify first that we deliver here support for the Cinema 4D APIs, so any Xpresso or Scene Nodes related solutions would be out of scope for this forum.

There are multiple ways how you can tackle your problem. When we just look at "quick-and-dirty" scripting object solutions, i.e., things that are not full-blown plugins, then there are the Python Programming Tag and the Python Generator Object. The former is a tag which can execute some Python code to modify the state of a scene. The latter is a little wrapper for a generator ObjectData plugin. It generates an object from scratch and by that implements an object that is not editable on a point level as for example the native Cube object of Cinema 4D. Your setup implies a Python Programming Tag solution, but it is likely that implementing a generator would be the better solution. However, I will focus on your setup for the rest of the answer.

There is also the problem that you talk about 'relevant' axis and your user data are named "height" which are both ambiguous terms. To make it short, figuring out a 'relevant' axis, i.e., the axis that humans would consider important in relation to another given axis for a given geometry is not trivial.

Additionally, there is the problem that such tag-based solution would not work well with generator objects as inputs, as you would have to touch the scale of the matrix of the object for that, which would lead to the object having a scale other than (1, 1, 1) in object mode and the parametric object being distorted in model mode without a clear indication why.

Below I provided a small example that probably does what you want to do or should give you a starting point. Without wanting to be rude, I must also point out that we cannot write code for you. So, for future postings you must provide code of your own with your questions which shows that you tackled the problem yourself.

Cheers,
Ferdinand

The result:
plane.gif
The file:
pc13739.c4d

The code:

"""Example for scaling a point object from a Python scripting object.
"""

import c4d

def main():
    """Sets the scale of the hosting object.
    """
    # op is a predefined module attribute in Python scripting environments. In
    # the case of a Python Programming tag it does reference the tag itself.
    # BaseTag.GetObject() returns the object a tag is attached to.
    pythonTag = op
    hostObject = pythonTag.GetObject()

    # Make sure that the tag is attached to an editable point object.
    if not isinstance(hostObject, c4d.PointObject):
        raise RuntimeError("Illegal host object.")

    # Get the #height user data value from the tag which is the first value.
    targetHeight = pythonTag[c4d.ID_USERDATA, 1]
    if not isinstance(targetHeight, float):
        raise AttributeError("Unexpected user data state.")

    # Get the bounding box vector of the object and the current length along
    # the "height", which I declared to be the y-axis. The value must be
    # multiplied by two because this is the bounding box radius, not the size.
    #
    #  ┌────────────┐
    #  │      ▴     │
    #  │      │     │
    #  │      └───▸ │
    #  │      r     │
    #  │            │
    #  └────────────┘
    #
    # 2D Bounding box of an object, in Cinema 4D it is of course 3D. r is
    # the bounding box vector of that bounding box.
    currentHeight = hostObject.GetRad().y * 2.

    # Determine the scaling factor with the current and target height and
    # build a scaling vector which leaves the other axis untouched.
    scaleY = targetHeight / currentHeight
    scale = c4d.Vector(1., scaleY, 1.)

    # Get all points of the point object and scale them. The ^ operator
    # denotes component-wise vector multiplication in the c4d Python API,
    # i.e., (1, 2, 3) ^ (3, 2, 1) == (1 * 3, 2 * 2, 3 * 1) == (3, 4, 3).
    # The * operator would be the dot product.
    scaledPoints = [p ^ scale for p in hostObject.GetAllPoints()]

    # Write back the scaled points and send an update message to the object.
    hostObject.SetAllPoints(scaledPoints)
    hostObject.Message(c4d.MSG_UPDATE)

Hello @bokibo,

welcome to the Plugin Café and the Cinema 4D development community and thank you for reaching out to us. I would propose that you look at our Forum Guidelines, as they explain the form of a posting and forum features. I have marked your posting as a question for you in this case, updated your tags a bit to better reflect what this is about, and also moved the topic into the correct forum.

About your question: Although you do not indicate anything like that, I would like to clarify first that we deliver here support for the Cinema 4D APIs, so any Xpresso or Scene Nodes related solutions would be out of scope for this forum.

There are multiple ways how you can tackle your problem. When we just look at "quick-and-dirty" scripting object solutions, i.e., things that are not full-blown plugins, then there are the Python Programming Tag and the Python Generator Object. The former is a tag which can execute some Python code to modify the state of a scene. The latter is a little wrapper for a generator ObjectData plugin. It generates an object from scratch and by that implements an object that is not editable on a point level as for example the native Cube object of Cinema 4D. Your setup implies a Python Programming Tag solution, but it is likely that implementing a generator would be the better solution. However, I will focus on your setup for the rest of the answer.

There is also the problem that you talk about 'relevant' axis and your user data are named "height" which are both ambiguous terms. To make it short, figuring out a 'relevant' axis, i.e., the axis that humans would consider important in relation to another given axis for a given geometry is not trivial.

Additionally, there is the problem that such tag-based solution would not work well with generator objects as inputs, as you would have to touch the scale of the matrix of the object for that, which would lead to the object having a scale other than (1, 1, 1) in object mode and the parametric object being distorted in model mode without a clear indication why.

Below I provided a small example that probably does what you want to do or should give you a starting point. Without wanting to be rude, I must also point out that we cannot write code for you. So, for future postings you must provide code of your own with your questions which shows that you tackled the problem yourself.

Cheers,
Ferdinand

The result:
plane.gif
The file:
pc13739.c4d

The code:

"""Example for scaling a point object from a Python scripting object.
"""

import c4d

def main():
    """Sets the scale of the hosting object.
    """
    # op is a predefined module attribute in Python scripting environments. In
    # the case of a Python Programming tag it does reference the tag itself.
    # BaseTag.GetObject() returns the object a tag is attached to.
    pythonTag = op
    hostObject = pythonTag.GetObject()

    # Make sure that the tag is attached to an editable point object.
    if not isinstance(hostObject, c4d.PointObject):
        raise RuntimeError("Illegal host object.")

    # Get the #height user data value from the tag which is the first value.
    targetHeight = pythonTag[c4d.ID_USERDATA, 1]
    if not isinstance(targetHeight, float):
        raise AttributeError("Unexpected user data state.")

    # Get the bounding box vector of the object and the current length along
    # the "height", which I declared to be the y-axis. The value must be
    # multiplied by two because this is the bounding box radius, not the size.
    #
    #  ┌────────────┐
    #  │      ▴     │
    #  │      │     │
    #  │      └───▸ │
    #  │      r     │
    #  │            │
    #  └────────────┘
    #
    # 2D Bounding box of an object, in Cinema 4D it is of course 3D. r is
    # the bounding box vector of that bounding box.
    currentHeight = hostObject.GetRad().y * 2.

    # Determine the scaling factor with the current and target height and
    # build a scaling vector which leaves the other axis untouched.
    scaleY = targetHeight / currentHeight
    scale = c4d.Vector(1., scaleY, 1.)

    # Get all points of the point object and scale them. The ^ operator
    # denotes component-wise vector multiplication in the c4d Python API,
    # i.e., (1, 2, 3) ^ (3, 2, 1) == (1 * 3, 2 * 2, 3 * 1) == (3, 4, 3).
    # The * operator would be the dot product.
    scaledPoints = [p ^ scale for p in hostObject.GetAllPoints()]

    # Write back the scaled points and send an update message to the object.
    hostObject.SetAllPoints(scaledPoints)
    hostObject.Message(c4d.MSG_UPDATE)

@bokibo This is very easy to implement using just Xpresso, rather than Python. Do you need to use Python?

I've attached a solution if it's helpful.
height_user_data_solved.c4d

Hello @bokibo,

without any further questions or postings, we will consider this thread as solved by Friday the 4th, February 2022.

Thank you for your understanding,
Ferdinand