Hide Input Child Objects in GetContour[SOLVED]

On 22/11/2016 at 15:32, xxxxxxxx wrote:

Hi,

1. How do I hide my child input objects? I'm using Touch() in GetContour() but it only works when I'm editing the child-objects.
2. How do I ensure that grandchildren don't reappear when nested in another generator?

I've created a simplified example plugin demonstrating my issues:
BitBucket Snippet of ScaleSplineObject.pyp

And, while harder to read, here's the full source embedded:

  
"""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 <[email protected]>   
  
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: https://plugincafe.maxon.net/topic/5237/5232_python-make-object-editable   
  
        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)   

Thanks!

On 22/11/2016 at 15:37, xxxxxxxx wrote:

These posts seem to be related:

- Object generator, GVO, cache and child objects - Plugin Cafe Forums
Solution doesn't work because GetContour() doesn't have access to DependenceList() nor HierarchyHelp.

- Touching Input object which in turn has input - Plugin Cafe Forums
I think this is the same problem I'm having, no resolution was ever posted.

- GetContour And op->Touch() - Plugin Cafe Forums
Seems to indicate that Touch() doesn't work properly in GetContour(), and that without access to HierarchyHelp, these will be long-standing issues.

---

Any thoughts? It seems like I have to choose between:

1. Use GetVirtualObjects so I can easily hide children, but then create a spline that is incompatible with Hair, SketchAndToon, and any other plugins that use GetRealSpline() as a test to see if something is a spline.
2. Use GetContour() and end up with flickering sub-objects and poor object hiding.

On 24/11/2016 at 02:51, xxxxxxxx wrote:

Hi Donovan, thanks for writing us.

with reference to your question, the solution proposed here should provide the functionality desired in terms of hiding child spline in generators.

Best, Riccardo.

On 28/11/2016 at 21:24, xxxxxxxx wrote:

Hi Riccardo,

Unfortunately, the example you've posted:

1. Does not hide child objects.
2. Only works with SplineObjects instead of Spline Primitives (try to add a Circle as a child for example).

Are you sure you posted the most recent version?

On 15/12/2016 at 13:08, xxxxxxxx wrote:

Hi Donovan,  thanks for writing us.

i've provided a new answer here with updated code which should hide the child objects and work with procedural primitives as well.

Best, Riccardo