Neighbor class and disconnected polygons



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 08:48, xxxxxxxx wrote:

    You are putting non-ASCII (non-printable) characters in the pseudo-string you are creating.  In other words, you _start_ by putting the value zero (null, effectively null-terminating the 'string') in the first position (not the ASCII value for '0'), then the value 1, then 2, then 3... these are all non-printable values.

    If you wanted to put actual printable characters in there, you'd still be hosed for any meshes with more than 10 polygons ('0' -> '9').... and keep in mind that the largest value a UCHAR can hold (printable or not) is 255.

    Basically, I think you may be thinking that 'reinterpret_cast' is doing something for you that it's not.

    To achieve what you're trying to achieve, I'd do something like:

      
        
        
            BaseObject *obj = doc->GetActiveObject();  //The active object  
          PolygonObject *pobj = ToPoly(obj);         //Cast the active object to a polygon type  
          
          
          //The Number of polygons in the active object      
          LONG numPolys = pobj->GetPolygonCount();  
          
          
          //Creates a LONG array with it's size based on the number of polygons  
          //At this point all the ***--> LONG <--*** elements are empty(null values)  
          LONG *PolyProcessed = (LONG * )GeAlloc(numPolys * sizeof(LONG));   
          
          String str;  
          for(LONG i=0; i<numPolys; i++)  
          {  
              PolyProcessed[i] = i; //<--This is supposed to fill the LONG array elements with numbers starting from zero  
          
                str += LongtoString(PolyProcessed[i]);
          
              GePrint(str);    
          }
    

    ...the above should give you the thing that your code was attempting (though I'm not sure how ultimately useful it would be - maybe just programing exercise?).

    EDIT: Note that I assumed that you wanted to GePrint() the entire PolyProcessed table - which could blow up GePrint() once it got large enough - if you just want to verify each element, replace the:

      
        
        
                str += LongtoString(PolyProcessed[i]);
        
    ...with...  
        
        
                str = LongtoString(PolyProcessed[i]);
        
    

    I'm using a UCHAR array basically as a 'boolean' table, with only 8bits = 1byte (sizeof(UCHAR)) per entry.  So 100,000 polygons would use 100,000 bytes of storage.  A BaseSelect is actually a lot more efficient than that, but does come with some processing overhead (it uses less memory, but is not as fast as a direct table look-up).  It's actually a bit of a moot point, since I doubt that you'd see any real-world difference in speed.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 09:58, xxxxxxxx wrote:

    Thanks.
    I'm new to this whole ASCII, Unicode, etc.. stuff. And I'm trying to figure it all out.

    -I still don't see how you're getting a BOOL value from a UCHAR array.
    -I would also like to know how to convert a UCHAR value to a String value. If that's possible.

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 10:25, xxxxxxxx wrote:

    Well... a Cinema 4D SDK "Bool" is actually typdef'd as an 'int', which would be a 32bit or 64bit value, but the typical use of such a variable is just to store true or false, 1 or 0... which only needs a single bit to represent, but the compiler/cpu can access a 32bit/64bit value just as fast.

    Since a Bool is either 32bits or 64bits (depending on the OS and Compiler options), I am using an 8bit 'unsigned character' (a UCHAR) instead, to save some memory.  I am just using it to store 1 or 0 - true or false.

    So, because of the way C/C++ works, I can test the byte for being 'non-zero'...

      
    UCHAR b = 0;  
      
    if( b ) ...  // would evaluate to false.  
      
    b = 1;  
      
    if( b ) ... // b is now 'non-zero', so it evaluates to true.  
      
    b = 255;  
      
    if( b ) ... // b is still non-zero, so it evaluates to true.  
      
    char *ptr = NULL;  
      
    if( ptr ) ... // since ptr is NULL ( zero ), it evaluates to false.  
      
    char mystring[] = "hello world";  
    ptr = mystring; // or ptr = &mystring[0];  
      
    if( ptr ) ... // since ptr now points to something, it has a non-zero/non-NULL value, so it evaluates to true.  
      
    String c4dStr = String(mystring);        // convert char C string to Cinema 4D String  
    String c4dStr = String("hello world"); // that also works  
    String c4dStr = "hello world";             // so does that  
      
    String c4dStr = "hello";  
    c4dStr += " world";                           // you can also concatenate...  
      
    String c4dStr = String("hello")+String(" world");  // but you might have to use the String() form to add/concatenate on the same statement.  
      
    

    ...basically any value except 0 (or NULL, for pointers) evaluates to true.

    For ASCII values, do a google search to find a chart like this one... where you can see (for example) the ASCII value for the character '0' is the value 48 decimal, so...

      
    char mystring[12];  
    mystring[0] =   72;  // H  
    mystring[1] =  101;  // e  
    mystring[2] =  108;  // l  
    mystring[3] =  108;  // l  
    mystring[4] =  111;  // o  
    mystring[5] =   32;  // <space>  
    mystring[6] =   87;  // W  
    mystring[7] =  111;  // o  
    mystring[8] =  114;  // r  
    mystring[9] =  108;  // l  
    mystring[10] =  100; // d  
    mystring[11] =    0; // null termination.  
      
    String c4dStr = String(mystring);  
    GePrint(c4dStr);   // prints...    "Hello World" (without the quotes)  
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 11:10, xxxxxxxx wrote:

    Thanks for the explanation.
    I understand how you're getting the BOOL now.👍

    I think I was misunderstanding what a UCHAR is. Please check my thinking on this.
    UCHAR is used to store values. Specifically positive values. Not characters as the name seems to imply.
    The same way a DWORD stores unsigned long values. Not strings as the name seems to imply.
    So there is no such thing as converting a UCHAR to a string.

    Is that correct?

    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 11:13, xxxxxxxx wrote:

    ...Just for completeness (I'm still not clear exactly what you're asking/trying to accomplish)...

    If you literally want to print the values of a UCHAR array, you can still do that fairly easily...

      
        GePrint(String("PolyProcessed[0] = ")+LongToString((LONG)PolyProcessed[0]));  
    

    ...you could even leave out the (LONG) cast (it would get cast anyway - I just added it for clarity).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 11:15, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Thanks for the explanation.
    I understand how you're getting the BOOL now.👍

    I think I was misunderstanding what a UCHAR is. Please check my thinking on this.
    UCHAR is used to store values. Specifically positive values. Not characters as the name seems to imply.
    The same way a DWORD stores unsigned long values. Not strings as the name seems to imply.
    So there is no such thing as converting a UCHAR to a string.

    Is that correct?

    -ScottA

    Yes - you are correct - I'm using it to just store some value between 0 - 255 (and in this case, just either 1 or 0).

    An array of UCHARs is actually a C string, by definition and can be used as such as long as it's null-terminated (erm... and as long as it contains printable characters :) - but not how I'm using it).

    Again, just for completeness/correctness... you can in fact store characters in a UCHAR array...

      
    mystring[0] =  'H';  // H  
    mystring[1] =  'e';  // e  
    mystring[2] =  'l';  // l  
    mystring[3] =  'l';  // l  
    mystring[4] =  'o';  // o  
    mystring[5] =  ' ';  // <space>  
    mystring[6] =  'W';  // W  
    mystring[7] =  'o';  // o  
    mystring[8] =  'r';  // r  
    mystring[9] =  'l';  // l  
    mystring[10] =  'd'; // d  
    mystring[11] = '\0'; // null termination.  
    

    ...the above is equivalent to my previous post, just using characters instead of the equivalent  ASCII values that represent those same characters.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 06/07/2012 at 12:05, xxxxxxxx wrote:

    Thanks a lot for explaining this stuff.
    I did some searching on WCHAR to see If it could help me learn more about UCHAR. But you've eplained it quite well.

    Thank you. 🍺
    -ScottA



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 03/08/2012 at 04:43, xxxxxxxx wrote:

    Just a heads up: there is a slight bug in your ConnectedPolyGroup.Init() method.  I've moved ++m_GroupCount to below ProcessConnectedPolys() or your groupIDs are set too high in that method.  Now it appears to be working as expected.

        Bool    done =                FALSE;  
      for(LONG polyNdx = 0L; polyNdx != m_numPolys; ++polyNdx)  
      {  
          if (m_pDstPolyProcessed[polyNdx])    continue;  
      
          // set group index/ID  
          m_pGroupIDs[polyNdx] =                m_GroupCount;  
          // mark this one off...  
          m_pDstPolyProcessed[polyNdx] =        1;  
          ++m_numMappedPolys;  
      
          // set all neighbors to same group index/ID  
          done =                                this->ProcessConnectedPolys();  
          // bump group index/ID for next group  
    // THIS HAS BEEN MOVED TO BELOW ProcessConnectedPolys()!  
          ++m_GroupCount;  
          if (done)                            break;  
      }
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 03/08/2012 at 10:03, xxxxxxxx wrote:

    Hmm... I don't see any difference in what you posted and the latest posted code above (?).  Maybe I found/fixed the post after you had already grabbed the code?  Or am I missing something?



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 03/08/2012 at 10:11, xxxxxxxx wrote:

    Hehe...... ;)  Must have grabbed an earlier version of the code.  Good that you found it too! :)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 03/08/2012 at 10:22, xxxxxxxx wrote:

    I thought that might be the case :).  I don't recall what other changes I made (and whether or not any changes were bug-fixes), so you might want to compare the rest of that to what you're using as well.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 03/08/2012 at 10:33, xxxxxxxx wrote:

    Just for completeness, here is the latest in-use code...

      
    class ConnectedPolyGroup  
    {  
    private:  
      Bool        m_bInit;  
      LONG        m_numVerts;  
      LONG        m_numPolys;  
      LONG        m_numMappedPolys;  
      LONG        m_GroupCount;  
      CPolygon    *m_pDstPolys;  
      UCHAR        *m_pDstPolyProcessed;  
      UCHAR        *m_pNeighborsAdded;  
      LONG        *m_pGroupIDs;  
      Neighbor    m_dstNbr;  
      
      Bool ProccessConnectedPolys(void);  
    public:  
      void FreeData(void);  
      BaseSelect *SelectGroup(LONG GroupID);  
      LONG NumPolys(void)                        { return m_numPolys; }  
      LONG NumConnectedGroups(void)            { return m_GroupCount; }  
      LONG GroupID(LONG polyNdx)                { if(!m_bInit || polyNdx >= m_numPolys) return NOTOK;    return  m_pGroupIDs[polyNdx]; }  
      Bool Init(PolygonObject *pPolyObj);  
      
      ConnectedPolyGroup(void);  
      ~ConnectedPolyGroup(void);  
    };  
      
    ConnectedPolyGroup::ConnectedPolyGroup(void)  
    {  
      m_bInit = false;  
      m_GroupCount = 0;  
      m_pDstPolyProcessed = NULL;  
      m_pNeighborsAdded = NULL;  
      m_pGroupIDs = NULL;  
    }  
      
    ConnectedPolyGroup::~ConnectedPolyGroup(void)  
    {  
      this->FreeData();  
    }  
      
    void ConnectedPolyGroup::FreeData(void)  
    {  
      bDelete(m_pDstPolyProcessed);  
      bDelete(m_pNeighborsAdded);  
      bDelete(m_pGroupIDs);  
      m_numMappedPolys = 0;  
      m_GroupCount = 0;  
      m_numVerts = 0;  
      m_numPolys = 0;  
      m_bInit = false;  
    }  
      
    // generate a BaseSelect based on connected GroupID - caller owns returned BaseSelect pointer  
    BaseSelect *ConnectedPolyGroup::SelectGroup(LONG GroupID)  
    {  
      if(!m_bInit || GroupID >= m_GroupCount)    return NULL;  
      
      BaseSelect *pSel = BaseSelect::Alloc();  
      if( !pSel )                                return NULL;  
      pSel->DeselectAll();    // <-- shouldn't actually be needed  
      
      LONG polyNdx;  
      for(polyNdx=0; polyNdx<m_numPolys; polyNdx++)  
      {  
          if( m_pGroupIDs[polyNdx] == GroupID )  
              pSel->Select(polyNdx);  
      }  
      return pSel;  
    }  
      
    Bool ConnectedPolyGroup::Init(PolygonObject *pPolyObj)  
    {  
      this->FreeData();  
      
      m_numVerts = pPolyObj->GetPointCount();  
      m_numPolys = pPolyObj->GetPolygonCount();  
      m_pDstPolys = pPolyObj->GetPolygonW();  
      
      // track which polys have been processed.  
      m_pDstPolyProcessed = bNew UCHAR[m_numPolys];  
      if( !m_pDstPolyProcessed )        return false;  
      
      // track which polys have had their neighbors added.  
      m_pNeighborsAdded = bNew UCHAR[m_numPolys];  
      if( !m_pNeighborsAdded )        return false;  
      
      // track which 'connected group' each poly belongs to  
      m_pGroupIDs = bNew LONG[m_numPolys];  
      if( !m_pGroupIDs )                return false;  
      
      // initialize Neighbor class  
      if( !m_dstNbr.Init(m_numVerts, m_pDstPolys, m_numPolys, NULL) )    return false;  
      
      ClearMem(m_pDstPolyProcessed, m_numPolys * sizeof(UCHAR), 0);  
      ClearMem(m_pNeighborsAdded, m_numPolys * sizeof(UCHAR), 0);  
      ClearMem(m_pGroupIDs, m_numPolys * sizeof(LONG), 0);  
      
      LONG polyNdx;  
      Bool done = false;  
      for(polyNdx=0; polyNdx<m_numPolys; polyNdx++)  
      {  
          if( m_pDstPolyProcessed[polyNdx] )    continue;  
      
          m_pGroupIDs[polyNdx] = m_GroupCount;                        // set group index/ID  
          m_pDstPolyProcessed[polyNdx] = 1;                            // mark this one off...  
          m_numMappedPolys++;  
      
          done = this->ProccessConnectedPolys();                        // set all neighbors to same group index/ID  
      
          m_GroupCount++;                                                // bump group index/ID for next group  
          if( done )    break;  
      }  
      
      // done with these...  
      bDelete(m_pDstPolyProcessed);  
      bDelete(m_pNeighborsAdded);  
      m_bInit = true;  
      return true;  
    }  
      
    Bool ConnectedPolyGroup::ProccessConnectedPolys(void)  
    {  
      Bool morePolys = true;  
      while( morePolys )  
      {  
          morePolys = false;  
          LONG polyNdx;  
          for(polyNdx=0; polyNdx<m_numPolys; polyNdx++)  
          {  
              if( !m_pDstPolyProcessed[polyNdx] )    continue;    // only adding/processing neighbors of previously matched/valid polys  
              if( m_pNeighborsAdded[polyNdx] )    continue;    // don't add/process neighbors if already added  
      
    //            CPolygon *pDstPoly = &m_pDstPolys[polyNdx];  
              PolyInfo *pDstPolyInfo = m_dstNbr.GetPolyInfo(polyNdx);  
              LONG side;  
              for(side=0; side<4; side++)  
              {  
                      LONG dstNbrNdx = pDstPolyInfo->face[side];  
                  if( dstNbrNdx == NOTOK )                continue;    // skip any sides that don't exist  
                  if( m_pDstPolyProcessed[dstNbrNdx] )    continue;    // only adding neighbors not already added  
      
                  m_pGroupIDs[dstNbrNdx] = m_GroupCount;        // all connected polys get the same index/ID  
                  m_pDstPolyProcessed[dstNbrNdx] = 1;            // mark this one off...  
                  m_numMappedPolys++;  
                  morePolys = true;                            // since we added / processed another poly, make sure we loop again to get _it's_ neighbors  
              }  
              m_pNeighborsAdded[polyNdx] = 1;                    // we've processed this polys neighbors  
      
              if( m_numMappedPolys == m_numPolys )  
              {  
                  morePolys = false;  
                  break;    // return true?  
              }  
          }  
      }  
      if( m_numMappedPolys == m_numPolys )  
          return true;  
      return false;  
    }  
    

    ...I _think_ the only difference in that and what's posted above is that I go ahead and free a couple (no-longer-needed) arrays inside Init() instead of waiting for the Destructor.

    As an aside, based on some later use of this base code (in this thread), I think that I determined that I didn't really need the m_pNeighborsAdded array/checking/tracking at all.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 04/08/2012 at 07:55, xxxxxxxx wrote:

    Will compare code and update as needed.  Thanks!


Log in to reply