void end( const TWallVertexVector& vb, TIntVector& polygon, TIntVector& ib ) { assert( polygonCount ); if( polygonCount == 1 ) { // trivial case, just output input polygon polygon.resize( 0 ); polygon.reserve( vertices.size() ); int idx0 = *vertices.begin(); int idx = idx0; do { polygon.push_back( idx ); const TIntVector& vnext = vertexNexts[idx]; assert( vnext.size() == 1 ); idx = vnext[0]; } while( idx != idx0 ); triangulator::process( vb, polygon, ib ); } else { // mark vertex types markVertexTypes(); // trace and triangulate the polygon(s) traceBorder( vb, polygon, ib ); } }
void traceBorder( const TWallVertexVector& vb, TIntVector& polygon, TIntVector& ib ) { static int traceID = 0; ++traceID; int idx0 = getBorderIndex(); assert( idx0 >= 0 && idx0 < vertexTypes.size() ); ib.resize( 0 ); polygon.resize( 0 ); polygon.reserve( vertices.size()/2 ); TIntVector localPolygon; localPolygon.reserve( 128 ); TIntVector localIB; localIB.reserve( 128 ); int idxPrev = idx0; int idx = idx0; int debugCounter = 0; int debugLoopCounter = 0; do { localIB.resize( 0 ); bool willFormLoop; do{ localPolygon.push_back( idx ); borderVertices.erase( idx ); vertexTraceID[idx] = traceID; assert( ++debugCounter <= vertices.size()*2 ); // Next vertex is the neighbor of current, that is not interior // and that is not the previous one. // When there are many possible ones, trace based on angle. int idxNext = -1; SVector2 prevToCurr = vb[idx] - vb[idxPrev]; if( prevToCurr.lengthSq() < 1.0e-6f ) prevToCurr.set( -0.01f, -0.01f ); TIntIntsMap::const_iterator it; it = vertexNexts.find( idx ); assert( it != vertexNexts.end() ); const TIntVector& vnext = it->second; int n = vnext.size(); float bestAngle = 100.0f; for( int i = 0; i < n; ++i ) { int idx1 = vnext[i]; if( idx1 != idxPrev && vertexTypes[idx1] != VTYPE_INTERIOR ) { //if( idx1 != idxPrev ) { SVector2 currToNext = vb[idx1] - vb[idx]; float ang = signedAngle2D( prevToCurr, currToNext ); if( ang < bestAngle ) { bestAngle = ang; idxNext = idx1; } } } assert( bestAngle > -4.0f && bestAngle < 4.0f ); assert( idxNext >= 0 ); willFormLoop = (vertexTraceID[idxNext] == traceID); // Optimization: if best angle is zero, then we're walking // in a straight line. Optimize out the current vertex. if( bestAngle == 0.0f && idx != idx0 && !willFormLoop ) { localPolygon.pop_back(); } idxPrev = idx; idx = idxNext; } while( !willFormLoop ); assert( localPolygon.size() >= 3 ); //if( localPolygon.size() < 3 ) { // return; //} assert( ++debugLoopCounter < vertices.size() ); if( idx == idx0 ) { // The polygon is simple or we found the last loop. // Triangulate local and append to results. triangulator::process( vb, localPolygon, localIB ); polygon.insert( polygon.end(), localPolygon.begin(), localPolygon.end() ); ib.insert( ib.end(), localIB.begin(), localIB.end() ); // We can have separated other loops. Try fetching them as well. idx0 = getBorderIndex(); if( idx0 == -1 ) { return; } else { localPolygon.resize( 0 ); idxPrev = idx0; idx = idx0; } } else { // The polygon must be complex, and we just found a closed loop. // Take only the loop, triangulate it, append to results, continue. TIntVector::const_iterator itLoopStart = std::find( localPolygon.begin(), localPolygon.end(), idx ); assert( itLoopStart != localPolygon.end() ); // append to results TIntVector loopPolygon( itLoopStart, localPolygon.end() ); triangulator::process( vb, loopPolygon, localIB ); polygon.insert( polygon.end(), loopPolygon.begin(), loopPolygon.end() ); ib.insert( ib.end(), localIB.begin(), localIB.end() ); // continue - remove the looped polygon from local localPolygon.resize( itLoopStart - localPolygon.begin() ); } } while( true ); }