Neighbor class and disconnected polygons
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 03/07/2012 at 23:39, xxxxxxxx wrote:
User Information:
Cinema 4D Version: R10-R12
Platform: Windows ; Mac OSX ;
Language(s) : C++ ;---------
There are times when my plugin must deal with polygon objects that have sets of polygons that are not connected to the main group of polygons such as the eyes of human or animal models (which are typically modeled separately with no edge/point connections to the rest of the body) or text where the letters are separate (again no point or edge connections between letters) but represented as a single polygon object.Using the Neighbor class (with BaseSelect set to NULL in the Init() method), these disconnected polygon sets are ignored - which is understandable. My question is this: If I include a BaseSelect for the disconnected polygon sets, would Neighbor return the information that represents the connection between these polygons (which are connected to each other just not the main group)?
Additionally, how would I best go about determining which polygons were NOT included in the original Neighbor initialization? The Neighbor class doesn't have a way to get the polygons that are included (or excluded) easily.
Thanks!
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 04/07/2012 at 01:00, xxxxxxxx wrote:
From my recent experience with the Neighbor class (GMTA ), I can say that... your statement/conclusion is actually incorrect. Initializing with Null does in fact generate Neighbor information for all points/polys of the mesh.
So, the trick is just in determining which polys belong to which connected groups - in other words, you'll need to access at least one poly (and all it's neighbors) from each connected group to work through all the polygons of the mesh.
Since I'm trying to access every polygon of the mesh anyway, what I do is create a Boolean table (actually a couple of arrays of CHARs, but you could also use a BaseSelect) to mark off polys as I access them...
//--------------------------------------------------------------------------------- // Note that this code was "cobbled together" (modified) from some existing code // and has never been compiled/tested - it's only meant as a pseudo-example. // // In particular, some initialization shown below is actually done elsewhere and // I didn't add any (example) code to free the arrays that are allocated, etc... //--------------------------------------------------------------------------------- class myClass { LONG m_numVerts; LONG m_numPolys; LONG m_numMappedPolys; CPolygon *m_pDstPolys; UCHAR *m_pDstPolyProcessed; UCHAR *m_pNeighborsAdded; Neighbor m_dstNbr; Bool CheckTriPoly(CPolygon *pDstPoly) { return true; }; // whatever you need to do/check about this poly Bool CheckQuadPoly(CPolygon *pDstPoly) { return true; }; // whatever you need to do/check about this poly Bool ProccessPolyNeighbors(void); Bool ProcessAllPolys(PolygonObject *pPolyObj); }; Bool myClass::ProcessAllPolys(PolygonObject *pPolyObj) { m_numVerts = pPolyObj->GetPointCount(); m_numPolys = pPolyObj->GetPolygonCount(); m_pDstPolys = pPolyObj->GetPolygonW(); // track which polys have been processed. m_pDstPolyProcessed = (UCHAR * )GeAlloc(m_numPolys * sizeof(UCHAR)); if( !m_pDstPolyProcessed ) { // handle_error(); return false; } // track which polys have had their neighbors added. m_pNeighborsAdded = (UCHAR * )GeAlloc(m_numPolys * sizeof(UCHAR)); if( !m_pNeighborsAdded ) { // handle_error(); return false; } ClearMem(m_pDstPolyProcessed, m_numPolys * sizeof(UCHAR), 0); ClearMem(m_pNeighborsAdded, m_numPolys * sizeof(UCHAR), 0); // initialize Neighbor class if( !m_dstNbr.Init(m_numVerts, m_pDstPolys, m_numPolys, NULL) ) return false; CPolygon *pDstPoly = &m_pDstPolys[polyNdx]; LONG polyNdx; m_numMappedPolys = 0; Bool bfound; Bool done = false; for(polyNdx=0; polyNdx<m_numPolys; polyNdx++) { if( m_pDstPolyProcessed[polyNdx] ) continue; CPolygon *pDstPoly = &m_pDstPolys[polyNdx]; if( pDstPoly->c != pDstPoly->d ) bfound = this->CheckQuadPoly(pDstPoly); else bfound = this->CheckTriPoly(pDstPoly); if( bfound ) { m_pDstPolyProcessed[polyNdx] = 1; // mark this one off... m_numMappedPolys++; if( m_numMappedPolys == m_numPolys ) return true; done = this->ProccessPolyNeighbors(); if( done ) return true; } } return false; } Bool myClass::ProccessPolyNeighbors(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 // if( do_something_with_neighbor_poly() ) // { // m_pDstPolyProcessed[dstNbrNdx] = 1; // mark this one off... // // since we added / processed another poly, make sure we loop again to get _it's_ neighbors // morePolys = true; // m_numMappedPolys++; // } } if( morePolys ) m_pNeighborsAdded[polyNdx] = 1; // we've processed this polys neighbors (and found/added/processed more) if( m_numMappedPolys == m_numPolys ) { morePolys = false; break; // return true? } } } if( m_numMappedPolys == m_numPolys ) return true; return false; }
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 04/07/2012 at 01:13, xxxxxxxx wrote:
The above sample code may be a little confusing... the original code is extremely more involved/complex/convoluted than that - comparing polygons (with potentially different vertex locations and different vertex indices and different orderings of indices and different orderings of indices within the neighbors, etc.) from 2 different meshes - so I stripped out a lot of code to cobble that together, but basically...
- the first routine walks through the entire poly list
- if it finds a poly that meets the criteria (which could just be 'any' poly), it calls the ProccessNeighborPolys() routine to process any/all of it's neighbors.
- the ProccessNeighborPolys() routine walks through the entire list (potentially multiple times) processing any/all neighbors of the ones found in the first routine, as well as any found by itself.
- by the time ProccessNeighborPolys() returns, it will have processed any/all polygons of some "connected-group" of polygons.
- the first routine then continues to look for polygons in other connected-groups and repeats the process until all polygons have been processed.
...what's potentially confusing about the above kinda depends on what you need to DO with each poly - and/or whether or not each poly needs to meet some criteria...
My sample code just returns true from both the CheckTriPoly() and CheckQuadPoly() routines and then it has some commented-out pseudo-code for the test inside the ProccessNeighborPolys() routine ("if( do_something_with_neighbor_poly() )"), but you may not need any test/criteria at all. If that's the case, then you might want to remove the conditional test from:if( morePolys ) m_pNeighborsAdded[polyNdx] = 1; // we've processed this polys neighbors (and found/added/processed more)
...and always set it to 1 at that point in the code.
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 04/07/2012 at 12:05, xxxxxxxx wrote:
... it occurred to me that some might ask: "if you're going through the entire poly list anyway, why not just do the:
for(polyNdx=0; polyNdx<m_numPolys; polyNdx++) { .... }
...loop and be done with it?"
I'm not sure what Robert's implementation is, but in my case, this initial loop (in ProcessAllPolys()) is looking for polygons that actually meet some criteria - it's trying to find polygons of one mesh that 'match' polygons of another mesh (either by point-position, or matching UVs, since the indices do not match)... Once it finds a single polygon (within some connected-group), it than calls the ProcessPolyNeighbors() routine - to match all neighbor polys (between the 2 meshes) simply based on their neighbor relationships - regardless of how out of whack the point positions might be (morphs) or how out of whack the UV-mapping is.
In other words, if I can find a single polygon correlation (on each connected-group of polygons), I can then remap the point-order of the 'target' mesh to match a 'template' mesh, despite any differences in shape/uv-mapping.
In this particular case (my "Remap Vertex Order" plugin from my Morph Mill suite), this new code has optimized the time it takes to do that operation by several orders of magnitude (the old code did nested loops, comparing every polygon with every other (yet to be matched) polygon - and they ALL had to either have similar point locations or matching UVs). So the old code could (embarrassingly) run for several minutes (and may ultimately fail to match them all) - the new code runs in ~0.002 seconds and doesn't fail :).
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 04/07/2012 at 13:20, xxxxxxxx wrote:
...bah... I was bored, so I just modified the code into a more complete (but simpler) real-world example...
//--------------------------------------------------------------------------------- // Note that this code was "cobbled together" (modified) from some existing code // for example purposes - it's now been compiled, but never tested :). //--------------------------------------------------------------------------------- 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); // don't have to, but can call this to free memory if done accessing it 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 (and must free) 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; } 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; }
...the above class should actually be functional/useful if you want to identify which polys belong to which connected-groups of polys.
> EDIT: fixed a couple bugs and added some functionality from original posting, so grab again if you think you got it before seeing this note.
>Cheers.
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 06/07/2012 at 00:30, xxxxxxxx wrote:
...just as an aside, I am now (successfully) using the above code in my Morph Mill plugin - I added a "Split by Connected Polys" command and I use the code above to get BaseSelects for each connected-poly group of the original mesh...
//------------------------------------------------------------------------------------------------------ //****************************************************************************************************** // SplitConnected() //****************************************************************************************************** //------------------------------------------------------------------------------------------------------ Bool GroupSplitter::SplitConnected(BaseDocument *pDoc, PolygonObject *op) { //-------------------------------------------------------------------------- // Make sure there are connected poly groups (more than one)... //-------------------------------------------------------------------------- ConnectedPolyGroup conPolyGrp; if( !conPolyGrp.Init(op) ) return false; LONG numGrps = conPolyGrp.NumConnectedGroups(); if( numGrps < 2 ) { GePrint("GroupSplitter::SplitConnected() - Less than 2 Connected Poly Groups present"); return false; } //-------------------------------------------------------------------------- // Initialize the ioMesh class (used by SplitSelection()) //-------------------------------------------------------------------------- if( !this->InitMesh(pDoc, op) ) return false; LONG i; for(i=0; i<numGrps; i++) { BaseSelect *pBS = conPolyGrp.SelectGroup(i); if( pBS ) { this->SplitSelection(pBS, String("PolyGroup_")+LongToString(i), String("_polygroups")); BaseSelect::Free(pBS); } } return true; }
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 06/07/2012 at 04:34, xxxxxxxx wrote:
Hey Giblet,
I'm not ignoring you but just had too many other things to do to try out the code. I see that all of the polygon neighbors are in the Neighbor class but not checked by my criteria. My critiera is looking at a 'seed' polygon and finding the direct neighbors level by level from there. This fails to include disconnected groups though. It may be that I want to use your grouping algorithm as a preprocess so that the indirect relation between groups (closest polygons and distance) can be used to make the continuum of levels that must jump these disconnected groups.
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 06/07/2012 at 04:52, xxxxxxxx wrote:
No problem - I figured you were busy with other things - I just tend to get hooked into some subject and get carried away with it at times :). You're welcome to use any of the above that you find useful.
Happy belated 4th!
-
THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED
On 06/07/2012 at 08:23, xxxxxxxx wrote:
I have a question about your examples Giblet.
I see that you are using UCHAR instead of BaseSelect. Which is something I wanted to learn how to do. But I'm having trouble figuring out how to use the UCHAR this way.I'm not getting any return from my print function.
Can you possibly look at this example, and the notes I've made in it. And tell me what I'm doing wrong?Example:
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 UCHAR array with it's size based on the number of polygons //At this point all the UCHAR elements are empty(null values) UCHAR *PolyProcessed = (UCHAR * )GeAlloc(numPolys * sizeof(UCHAR)); for(LONG i=0; i<numPolys; i++) { PolyProcessed[i] = i; //<--This is supposed to fill the UCHAR array elements with numbers starting from zero String str; str.SetCString(reinterpret_cast<const CHAR*>(PolyProcessed), -1, STRINGENCODING_8BIT); //<--This is probably wrong? GePrint(str); }
Thanks,
-ScottA -
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.