Exporting Selections



  • On 11/01/2017 at 07:54, xxxxxxxx wrote:

    Hi!

    I'm using C4D R16 and I'm trying to write a short script that goes through all edge and polygon selection tags in the selected objects and exports the data of the selected edges to an external file. The documentation however is very superficial.

    So far I have tried 2 approaches:
    The BaseSelect  object has a write method that uses a HyperFile. This output some data into a file. The file seemed to contain some extra information beyond the edges and polygons, as well as a large number of padding 0-s or 0-s that denoted who knows what. And not knowing how edge data is stored (I assumed originally that each edge would be 2 ints for the 2 end points' id-s, but the file didn't look like that, it contained too few non-zero words), I had no way of interpreting the data.

    The second approach was to use BaseSelect.GetAll() and output the data with the built in IO functions of Python as text. The latter part worked fine, but GetAll() returned a series of 0-s instead of useful data. For the edges it output twice the number of selected edges, for the polygons it output as many 0-s as the olygons selected. This suggests to me that my initial assumption on edges couldn't have been too far off and polygons are stored with id-s that can be used to look up their vertices in which case what was up with the HyperFile?

    My questions:
    - How does C4D store polygon and edge data?
    - Is there a more extensive documentation on the BaseSelect class than the one in the official documentation of the SDK? The official one is neat, but lacks any sort of information on data structures.
    - What's up with all the 0-s?
    - Is what I'm doing feasible at all and off only by overlooking a necessary step or two, or should I try a completely different approach? Is there one?

    Thanks,
    zoliking



  • On 12/01/2017 at 07:38, xxxxxxxx wrote:

    Hi zoliking,

    welcome to the Plugin Café forums 🙂

    Unfortunately you didn't tell us, what's your final use-case, so it's hard to tell, what's the optimal solution.

    - How does C4D store polygon and edge data?

    C4D doesn't store edge data at all. Edges are a result of the stored polygons, which are stored as an array of CPolygon. CPolygon then stores up to four point indices.

    - Is there a more extensive documentation on the BaseSelect class...?

    I like your paraphrase for "the docs suck!" 🙂
    Unfortunately there's currently no other documentation, than the one you already know. At least none that I know of. Often it can be a good idea to take a look at the C++ documentation, which might contain more information, especially because we are currently trying to extend it.

    - What's up with all the 0-s?

    Well, it's hard to tell without knowing your scenario, but I'd expect it to be a lot of zeros, depending on the selection state. After all a BaseSelect doesn't store anything but zeroes (false) and ones (true)...
    So, like in a famous German movie we could ask "What is a BaseSelect?"
    The BaseSelect class actually is nothing (well, almost nothing) more than an array of boolean values storing a selection state per index. While mostly used to store polygon or point selections, it can (and actually is internally) used to store all kinds of selections.

    I think, the index is the actual problem you are facing.
    When retrieving the BaseSelect from an Edge Selection tag, one might expect something like an edge index, but as said before, there are actually no edges in C4D. Instead the information is stored on a polygon/edge basis. For every polygon there are four entries (for every possible edge) in the BaseSelect.

    Four entries per polygon in the BaseSelect (this is only true for Edge selections!). So you can either index the BaseSelect via "poly index * 4 + edge index" or the other way round for a given BaseSelect index the polygon index will be "bsIdx / 4" and the edge index on this polygon "bsIdx % 4".

    Via the edge index you can get hold of the actual point indices via EdgePoints() function.

        baseSelect = tag.GetBaseSelect()
        obj = tag.GetObject()
        polyCount = obj.GetPolygonCount()
        
        for e in xrange(polyCount * 4) :
            if baseSelect.IsSelected(e) :
                print "Poly: ", e/4, "Edge index:", e%4, "Edge point index a - b: ", obj.GetPolygon(e/4).EdgePoints(e%4)[0], " - ", obj.GetPolygon(e/4).EdgePoints(e%4)[1]
    

    Finally there's one more peculiarity you need to know. As the information is stored on a polygon basis, a edge may be "selected" several times, if it's shared by several polygons.

    - Is what I'm doing feasible at all...?

    This is probably a bit difficult to answer without knowing what you are trying to achieve in the end.

    If you are just trying to move the data out of and then back into C4D, I'd stick to the hyperfile approach. There you wouldn't have to care about the indices at all (and it should simply work out of the box). But as you already found out a hyperfile is not a plain file. There are data type IDs stored with every entry, for some data also length information. These files are not thought to be read by other applications.

    So, if you want to exchange data with another application, you are probably better off, getting the information in the way described above and then write it to your own file format in what ever way you need.

    I hope, I was able to shed some light.



  • On 16/01/2017 at 01:09, xxxxxxxx wrote:

    Thank you very much for that information, it sounds useful.

    My end goal is that I'm trying to write a proof of concept test program for a game idea I have and in that some edges and polygons in certain models are special, that's why I need to export selections from C4D.

    My "all the 0-s" question wasn't specific enough though, it related to the fact that all returned values were 0 when I worked with nonzero selections in my second attempt.

    Here is my code:

    def main() :
      objList = doc.GetActiveObjects(True)
      obj = objList[0]
     
      tag = obj.GetFirstTag()
     
      dataFile = open("Drive:/MyPath/MyFile.txt", "w")

    while tag:
          if (tag.GetType() == c4d.Tpolygonselection or tag.GetType() == c4d.Tedgeselection) :
              selectionTag = tag.GetBaseSelect()
              dataList = selectionTag.GetAll(selectionTag.GetCount())
              for data in dataList:
                  dataFile.write(str(data))
              dataFile.write('\n')
          tag = tag.GetNext()
     
      dataFile.close()

    if __name__=='__main__':
      main()

    Thanks again for the information!



  • On 19/01/2017 at 08:35, xxxxxxxx wrote:

    Any ideas anyone? GetAll() should return non zero integers for non zero selections ideally, right?



  • On 20/01/2017 at 00:56, xxxxxxxx wrote:

    Everything as been said above. Here is a working example.

    import c4d
      
    def main() :
        objList = doc.GetActiveObjects(True)
        obj = objList[0]
        
        tag = obj.GetFirstTag()
        
      
        poly_selected = list()
        edge_selected = list()
        if not isinstance(obj, c4d.PointObject) : #Mayvbe you should check PolygonObject depending of what you want to do
            return
        
        while tag:
            if tag.GetType() == c4d.Tpolygonselection:
                points_count = obj.GetPolygonCount()
                selectionTag = tag.GetBaseSelect()
                
                dataList = selectionTag.GetAll(points_count)
                for i in xrange(points_count) :
                    if dataList[i]:
                        poly_selected.append(i)
            
            elif tag.GetType() == c4d.Tedgeselection:
                points_count = obj.GetPolygonCount() * 4 #Read Andreas post
                selectionTag = tag.GetBaseSelect()
                
                dataList = selectionTag.GetAll(points_count)
                for i in xrange(points_count) :
                    if dataList[i]:
                        if i%4: #remove edge with id 0
                            edge_selected.append(i%4)
                    
            tag = tag.GetNext()
            
        print poly_selected
        print list(set(edge_selected)) #Remove duplicate entry
      
      
    if __name__=='__main__':
        main()
    

    You have wrote selectionTag.GetCount()) but the count have to be done on poly/point count not the count of the tag.

    It's look like edge 0 is always returned. Is it normal?



  • On 20/01/2017 at 08:53, xxxxxxxx wrote:

    What do you mean, edge 0 is always returned? I haven't seen anything special with edge 0, when doing my tests here.



  • On 20/01/2017 at 09:57, xxxxxxxx wrote:

    Ok that was a miss understand from me.
    I was thinking edge id are unique for each edge (as a point index).

    But as you said they belong to a polygon id or two point id.
    So the only way for checking if we got twice the same edge is by getting edge from 2 point id?



  • On 22/01/2017 at 10:42, xxxxxxxx wrote:

    Thank you!


Log in to reply