Snippets

Donovan Keith C4D Python SDK Python Spline Generator Object Input Example (WARNING: Not fully functional)

Created by Donovan Keith
"""Scale Spline Object
Takes a child-spline as input and scaled it's point's down by 50%. Tangents are unaffected.

v0.0.1

by Donovan Keith <donovanskeith@gmail.com>

Usage Instructions
------------------
1. Save in a file called ScaleSplineObject.pyp
2. Place in C4D Plugins director
3. Restart C4D
4. Plugins > Scale Spline Object
5. Add an NSide spline
6. Make the NSide spline a child of the Scale Spline Object.

Outstanding Issues
-------------------

- [ ] Nested child objects aren't properly hidden. To recreate:
        ScaleSpline
        |
        -- Scale Spline
            |
            -- NSide
- [ ] GetDirty() probably needs to be reimplemented.
"""

# =====================================================================================================================#
#   Imports
# =====================================================================================================================#

import c4d


# =====================================================================================================================#
#   Class Definitions
# =====================================================================================================================#

class ScaleSplineObject(c4d.plugins.ObjectData):
    PLUGIN_ID = 1038342  # TODO: Replace with your own ID from PluginCafe.com

    def __init__(self):
        self.last_child = None

    def Init(self, op):
        return True

    def ConversionCommand(self, op, command_id):
        """Allows you to easily perform a MakeEditable or CSTO on an object.
        reference: http://www.plugincafe.com/forum/forum_posts.asp?TID=5232

        Returns early if op is a Spline
        """

        if op is None:
            return

        op_clone = op.GetClone()
        if op_clone is None:
            return

        # Is it a spline? No need to convert.
        if op_clone.CheckType(c4d.Ospline):
            return op_clone

        # Perform the MakeEditable or CSTO in temporary document
        temp_doc = c4d.documents.BaseDocument()
        if not temp_doc:
            return

        temp_doc.InsertObject(op_clone)
        result_list = c4d.utils.SendModelingCommand(command=command_id,
                                                    list=[op_clone],
                                                    doc=temp_doc)

        # Did anything go wrong? Bail
        if not result_list:
            return

        # Return the first object in the result list
        return result_list[0]

    def MakeEditable(self, op):
        """Returns a clone of `op` that's been made editable. Can return None."""
        return self.ConversionCommand(op, command_id=c4d.MCOMMAND_MAKEEDITABLE)

    def CurrentStateToObject(self, op):
        """Returns a clone of the current state of `op`. Can return None."""
        return self.ConversionCommand(op, command_id=c4d.MCOMMAND_CURRENTSTATETOOBJECT)

    def GetAsSpline(self, op):
        """Clones object as a c4d.Ospline - using the following priority
        * Object if Spline
        * Object Made Editable, if Spline
        * Current State to Object, if Spline

        Note: MakeEditable won't account for deformed splines, however, it will give you a spline
        with fewer points which is preferable to CSTO for a lot of modeling operations.
        """

        if op is None:
            return

        # Return op if it's a spline
        if op.CheckType(c4d.Ospline):
            return op.GetClone()

        # Make it editable if it is.
        made_editable = self.MakeEditable(op)
        if made_editable is not None:
            if made_editable.CheckType(c4d.Ospline):
                return made_editable

        # Peform a CSTO if need be
        # TODO: See if this ever actually executes, probably doesn't
        current_state = self.CurrentStateToObject(op)
        if current_state is not None:
            if current_state.CheckType(c4d.Ospline):
                return current_state

    def LocalizeSpline(self, op, spline):
        """Converts all of `spline`'s point to local space for `op`
        This is necessary because GetContour assumes the spline is in the same
        location as the object itself.
        """

        # Verify Inputs
        if (op is None) or (spline is None):
            return

        # Get points
        spline_points = spline.GetAllPoints()
        if not spline_points:
            return

        has_tangents = (spline.GetTangentCount() > 0)
        spline_tangents = []

        # Retrieve matrices for conversion math
        spline_mg = spline.GetMg()
        if not spline_mg:
            return

        op_mg = op.GetMg()
        if not op_mg:
            return

        inverse_op_mg = ~op_mg

        # Localize all points
        for i, spline_point in enumerate(spline_points):
            global_point = spline_point * spline_mg
            local_point = global_point * inverse_op_mg
            spline_points[i] = local_point

            # Calculate new tangent positions
            if has_tangents:
                # Get tangents for point
                tangent = spline.GetTangent(i)
                vl = tangent["vl"]
                vr = tangent["vr"]

                # Transform tangents into their global locales
                global_vl = (spline_point + vl) * spline_mg
                global_vr = (spline_point + vr) * spline_mg

                # Transform tangents from global points to point-local vectors.
                vl = (global_vl * inverse_op_mg) - local_point
                vr = (global_vr * inverse_op_mg) - local_point

                # Store the updated points & tangents.
                spline_tangents.append((vl, vr))
                spline.SetTangent(i, vl, vr)

        # Neutralize spline's Matrix
        spline.SetMg(op_mg)

        # Restore spline to previous positions
        spline.SetAllPoints(spline_points)

        # Update tangent positions as well
        for i, tangent in enumerate(spline_tangents):
            vl, vr = tangent
            spline.SetTangent(i, vl, vr)

    def CheckDirty(self, op, doc):
        """Marks the object as dirty if it's child object has been modified."""

        if not op:
            return

        # Is there a new or missing child object?
        child = op.GetDown()
        if child != self.last_child:
            # Dirty!
            self.last_child = child
            op.SetDirty(c4d.DIRTY_DATA)
            return

        # Can't do anythng without a child object.
        if child is None:
            return

        # Has the child ben modified?
        if child.IsDirty(c4d.DIRTYFLAGS_MATRIX |
                         c4d.DIRTYFLAGS_DATA |
                         c4d.DIRTYFLAGS_CHILDREN):
            # Dirty!
            op.SetDirty(c4d.DIRTYFLAGS_DATA)

    def ModifySpline(self, spline):
        """Scales all spline points down by 50%"""

        point_scaling_factor = 0.5

        if spline is None:
            return

        spline_points = spline.GetAllPoints()
        if spline_points is None:
            return

        for i, spline_point in enumerate(spline_points):
            spline_points[i] = spline_point * point_scaling_factor

        spline.SetAllPoints(spline_points)

    def GetContour(self, op, doc, lod, bt):
        """Return the result of the generator spline."""

        # Create an Empty Spline
        if not op:
            return

        # Retrieve the child input object
        child = op.GetDown()
        if child is None:
            return

        # Tell C4D the child is a generator input object and should be hidden.
        child.Touch()

        # Get the child input object as a spline clone we can modify.
        result_spline = self.GetAsSpline(child)
        if result_spline is None:
            return

        # Scale down the spline points.
        self.ModifySpline(result_spline)

        # Ensure the spline points are localized to this generator's PSR.
        self.LocalizeSpline(op, result_spline)

        return result_spline


# =====================================================================================================================#
#   Registration
# =====================================================================================================================#

if __name__ == "__main__":
    c4d.plugins.RegisterObjectPlugin(id=ScaleSplineObject.PLUGIN_ID,
                                     str="ScaleSplineObject",
                                     g=ScaleSplineObject,
                                     description=None,
                                     icon=None,
                                     info=c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT | c4d.OBJECT_ISSPLINE)

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.