Data to Spline



  • Hi everybody,

    My first post here so I really hope you tolerate my mistakes if I make any.

    I'm working on a path generation tool (for Windows and macOS) that creates paths with x and y coordinates of each points. I can easily covert them to SVG (using 'polyline' as it's more convenient than 'path' for my case). As far as I know, C4D doesn't support direct SVG import; it either needs to be *.dwg or *.dxf. Instead of expecting users to import the generated file into their scenes, I'd like to write a script that would do that with one click. Here are my questions:

    • Should I do that in Python or in C++? I'm not new to programming but I'm mostly a JAVA coder. I think I can give that a try on either of those languages.
    • What should be the data structure in the generated file? For what I have now, I'm getting values as [x,y] in floating point numbers like:
    540.3475, 417.24466
    541.6318, 417.6269
    544.0376, 417.85046
    548.4767, 417.7466
    

    How can I get those values from a *.txt file to create a spline in C4D? I can change the format of the values if needed. I just need to know the workaround (or a piece of script) to do that. Any help would be much appreciated.



  • Hi @allenrob,

    thank you for reaching out us. Also a big thank you @Cairyn and @x_nerve for the provided community support. I'll answer in bullet points:

    1. There is an importer plugin for SVG files for Cinema. I am not sure if it will fit your needs.
    2. When you want to have more control over the import process or use a custom file format, you can:
      a. Simply write your own interface as a script solution, I have provided a very simple example for the example data you did provide at the end of the posting. I just put your data into a txt file and read it in CSV style, but without using any special library. The example mostly demonstrates Python's very straight forward file object interface open.
      b. You can also use any of the standard library modules like json, csv, xml.etree.ElementTree to read in common data formats or use external libraries, e.g. pandas, to do so.
    3. Instead of just creating a script solution, like I did provide, you can also write a c4d.plugins.SceneLoaderData plugin. Read its documentation for details. You can do here basically anything you want and have a more natural user experience of loading in a file. Internally you would do mostly the same as in 2.a. or 2.b.

    I hope this helps,
    Ferdinand

    Your test data as a txt file to load in with the script below: points.txt

    """ Basic Python file IO to create a Cinema 4D linear spline from a CSV
    style file as discussed in:
    
        https://plugincafe.maxon.net/topic/13065
    
    Run this file in Cinema 4D's script manager (Shift+F11). It will open a file
    dialog, where you should locate the provided example file 'points.txt',
    which ten will be loaded into the document as a linear spline.
    
    !THIS IS AN EXAMPLE AND NOT PRODCUTION READY CODE!
    """
    
    import c4d
    import decimal
    
    def read_spline_from_file(file_path):
        """Reads one of your CSV style data structures into a single spline.
        """
        # The points of the spline we are going to build.
        points = []
        # Open a file object in read only text mode and read it line by line.
        with open(file_path, "rt") as file:
            for line in file.read().splitlines():
                # Parse the line in a comma separated value style.
                components = line.split(",")
                if len(components) != 2:
                    raise RuntimeError("Parsing Error")
                # Cast the components to floats. We need these gymnastics, because 
                # Python's string type cannot implicitly be cast into a float and 
                # Cinema does expect that type when instantiating a vector.
                components = [float(decimal.Decimal(c)) for c in components]
                # Create the vector and add it to our points list.
                p = c4d.Vector(*components, 0)
                points.append(p)
    
        # Create a spline object with our points.
        spline = c4d.SplineObject(pcnt=len(points), type=c4d.SPLINETYPE_LINEAR)
        spline.SetAllPoints(points)
        spline.Message(c4d.MSG_UPDATE)
        # Insert it into the active document.
        doc.InsertObject(spline)
        # Tell Cinema to update.
        c4d.EventAdd()
    
    def main():
        """Entry point.
        """
        # Open a file dialog.
        file_path = c4d.storage.LoadDialog(type=c4d.FILESELECTTYPE_ANYTHING, 
                                           title="Select a file.", 
                                           flags=c4d.FILESELECT_LOAD, 
                                           force_suffix="", 
                                           def_path="", 
                                           def_file="")
        # Read in the data as a single spline and insert into the scene.
        read_spline_from_file(file_path)
    
    if __name__ == "__main__":
        main()
    


  • Python can easily read lines from a file, split the line into partial strings, and convert the strings into float values.

    But for creating a spline from that, you will need to have a look at the class SplineObject and its parent PointObject:
    https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d/C4DAtom/GeListNode/BaseList2D/BaseObject/PointObject/SplineObject/index.html?highlight=spline#c4d.SplineObject

    C++ would require the huge project overhead so I can't really recommend this for such a simple script.



  • Hi Cairyn,

    I think I'm lost. I feel stuck. Is there way to get this done by paying somebody? I'm sorry, I know that's not the right place to list a job but python coders for C4D is extremely rare. :/



  • Hi:

    I think it might be faster to try to import the point location directly as an external library (.py). At the same time, C4D supports importing Illustrator files, and SVG can be processed in Illustrator before importing C4D. Here is a simple SplineObject code.

    import c4d
    
    def main():
        #The location of the point.
        Points = [c4d.Vector(0, 0, 0),c4d.Vector(0, 0, 2),c4d.Vector(0, 4, 4)]
        
        #Initializes the spline object.
        Spline = c4d.SplineObject(len(Points), c4d.SPLINETYPE_LINEAR
    )
        
        #Change the number of spline object segments, the number of points.
        Spline.ResizeObject(len(Points), 3)
        
        #Set the locations of all the points of the spline object.
        Spline.SetAllPoints(Points)
        
        #Set properties of the segment.
        Spline.SetSegment(0, len(Points), True)
        
        #Close the spline object.
        Spline[c4d.SPLINEOBJECT_CLOSED] = 1
        
        #Inserts the spline object into the current document.
        doc.InsertObject(Spline)
        
        #Update current document, routine code.
        c4d.EventAdd()
     
    if __name__=='__main__':
        main()
    


  • Hi @allenrob,

    thank you for reaching out us. Also a big thank you @Cairyn and @x_nerve for the provided community support. I'll answer in bullet points:

    1. There is an importer plugin for SVG files for Cinema. I am not sure if it will fit your needs.
    2. When you want to have more control over the import process or use a custom file format, you can:
      a. Simply write your own interface as a script solution, I have provided a very simple example for the example data you did provide at the end of the posting. I just put your data into a txt file and read it in CSV style, but without using any special library. The example mostly demonstrates Python's very straight forward file object interface open.
      b. You can also use any of the standard library modules like json, csv, xml.etree.ElementTree to read in common data formats or use external libraries, e.g. pandas, to do so.
    3. Instead of just creating a script solution, like I did provide, you can also write a c4d.plugins.SceneLoaderData plugin. Read its documentation for details. You can do here basically anything you want and have a more natural user experience of loading in a file. Internally you would do mostly the same as in 2.a. or 2.b.

    I hope this helps,
    Ferdinand

    Your test data as a txt file to load in with the script below: points.txt

    """ Basic Python file IO to create a Cinema 4D linear spline from a CSV
    style file as discussed in:
    
        https://plugincafe.maxon.net/topic/13065
    
    Run this file in Cinema 4D's script manager (Shift+F11). It will open a file
    dialog, where you should locate the provided example file 'points.txt',
    which ten will be loaded into the document as a linear spline.
    
    !THIS IS AN EXAMPLE AND NOT PRODCUTION READY CODE!
    """
    
    import c4d
    import decimal
    
    def read_spline_from_file(file_path):
        """Reads one of your CSV style data structures into a single spline.
        """
        # The points of the spline we are going to build.
        points = []
        # Open a file object in read only text mode and read it line by line.
        with open(file_path, "rt") as file:
            for line in file.read().splitlines():
                # Parse the line in a comma separated value style.
                components = line.split(",")
                if len(components) != 2:
                    raise RuntimeError("Parsing Error")
                # Cast the components to floats. We need these gymnastics, because 
                # Python's string type cannot implicitly be cast into a float and 
                # Cinema does expect that type when instantiating a vector.
                components = [float(decimal.Decimal(c)) for c in components]
                # Create the vector and add it to our points list.
                p = c4d.Vector(*components, 0)
                points.append(p)
    
        # Create a spline object with our points.
        spline = c4d.SplineObject(pcnt=len(points), type=c4d.SPLINETYPE_LINEAR)
        spline.SetAllPoints(points)
        spline.Message(c4d.MSG_UPDATE)
        # Insert it into the active document.
        doc.InsertObject(spline)
        # Tell Cinema to update.
        c4d.EventAdd()
    
    def main():
        """Entry point.
        """
        # Open a file dialog.
        file_path = c4d.storage.LoadDialog(type=c4d.FILESELECTTYPE_ANYTHING, 
                                           title="Select a file.", 
                                           flags=c4d.FILESELECT_LOAD, 
                                           force_suffix="", 
                                           def_path="", 
                                           def_file="")
        # Read in the data as a single spline and insert into the scene.
        read_spline_from_file(file_path)
    
    if __name__ == "__main__":
        main()
    


  • Hi,

    without further feedback, we will consider this thread as solved by Wednesday and flag it accordingly.

    Cheers,
    Ferdinand


Log in to reply