不規(guī)則多邊形三角切割法 OC版

openGL ES 項(xiàng)目中需要繪制不規(guī)則的多邊形,但openGL ES只支持三角形,三角帶,三角扇。但后臺(tái)給的數(shù)據(jù)只有頂點(diǎn)坐標(biāo)沒有頂點(diǎn)索引,只能根據(jù)頂點(diǎn)坐標(biāo)把多邊形切割成三角形:

耳切法切割多邊形的幾個(gè)主要方法:

1.根據(jù)頂點(diǎn)坐標(biāo)計(jì)算多邊形帶方向面積,主要是為了判斷頂點(diǎn)時(shí)針?lè)较颍龝r(shí)針面積<0,逆時(shí)針>0

+ (float)Area:(NSArray<NSValue *> *)contour {

    int n = (int)contour.count;
    
    float A = 0.0f;
    
    for(int p = n - 1, q = 0 ; q < n; p = q++) {
        
        CGPoint pPoint = [contour[p] CGPointValue];
        CGPoint qPoint = [contour[q] CGPointValue];
        
        A += (pPoint.x * qPoint.y - qPoint.x * pPoint.y);
    }
    return A * 0.5f;
}

2.判斷一個(gè)點(diǎn)是否在一個(gè)三角形(三個(gè)點(diǎn))之中

+ (BOOL)InsideTriangle:(float)Ax :(float)Ay :(float)Bx :(float)By :(float)Cx :(float)Cy :(float)Px :(float)Py {

    float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
    float cCROSSap, bCROSScp, aCROSSbp;
    
    ax = Cx - Bx;  ay = Cy - By;
    bx = Ax - Cx;  by = Ay - Cy;
    cx = Bx - Ax;  cy = By - Ay;
    apx= Px - Ax;  apy= Py - Ay;
    bpx= Px - Bx;  bpy= Py - By;
    cpx= Px - Cx;  cpy= Py - Cy;
    
    aCROSSbp = ax*bpy - ay*bpx;
    cCROSSap = cx*apy - cy*apx;
    bCROSScp = bx*cpy - by*cpx;
    
    BOOL result = (aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f);
    return result;
}
  1. 是否符合切割條件
static const float EPSILON = 0.0000000001f;
+ (BOOL)Snip:(NSArray<NSValue *> *)contour :(NSMutableArray *)V :(int)u :(int)v :(int)w :(int)n
{
    float Ax, Ay, Bx, By, Cx, Cy, Px, Py;

    int a = [V[u] intValue];
    CGPoint aPoint = [contour[a] CGPointValue];
    Ax = aPoint.x;
    Ay = aPoint.y;

    int b = [V[v] intValue];
    CGPoint bPoint = [contour[b] CGPointValue];
    Bx = bPoint.x;
    By = bPoint.y;

    int c = [V[w] intValue];
    CGPoint cPoint = [contour[c] CGPointValue];
    Cx = cPoint.x;
    Cy = cPoint.y;

    float temp = ((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax));
    
    if ( EPSILON > temp ) 
        return false;

    for (int p = 0; p < n ; p++) {
        
        if( (p == u) || (p == v) || (p == w) ) continue;
        
        int z = [V[p] intValue];
        CGPoint pPoint = [contour[z] CGPointValue];
        Px = pPoint.x;
        Py = pPoint.y;
        
        if ([self InsideTriangle:Ax :Ay :Bx :By :Cx :Cy :Px :Py]) 
            return false;
    }
    return true;
}
  1. 流程
+ (NSMutableArray *)Process:(NSArray<NSValue *> *)contour
{
    NSMutableArray *result = [NSMutableArray array];
    
    NSInteger n = contour.count;
    if ( n < 3 ) return result;
    
    NSMutableArray *V = [NSMutableArray arrayWithCapacity:n];
    
    if ( 0.0f < [self Area:contour] ) {
        for (int v = 0; v < n; v++) {
            NSNumber *num = [NSNumber numberWithInt:v];
            [V insertObject:num atIndex:v];
        }
    } else {
        for(int v = 0; v < n; v++) {
            NSNumber *num = [NSNumber numberWithInt:(int)(n-1)-v];
            [V insertObject:num atIndex:v];
        }
    }
    
    int nv = (int)n;
    
    int count = 2*nv;
    
    for(int v = nv-1; nv > 2; ) {

        if (0 >= (count--)) {
            return result;
        }
        
        int u = v; if (nv <= u) u = 0;    // i - 1
        v = u+1; if (nv <= v) v = 0;      // i
        int w = v+1; if (nv <= w) w = 0;  // i + 1
        
        if ( [self Snip:contour :V :u :v :w :nv]) {
            int a,b,c;
            
            a = [V[u] intValue];
            b = [V[v] intValue];
            c = [V[w] intValue];
            
            // 把切下的三角形放入數(shù)組
            [result addObject:contour[a]];
            [result addObject:contour[b]];
            [result addObject:contour[c]];
            
            [V removeObjectAtIndex:v];
            nv--;
            
            count = 2*nv;
        }
    }
    return result;
}
  1. 測(cè)試
- (void)test
{
    CGPoint a = CGPointMake(2, 2);
    NSValue *value1 = [NSValue valueWithCGPoint:a];
    CGPoint b = CGPointMake(2, -1);
    NSValue *value2 = [NSValue valueWithCGPoint:b];
    CGPoint c = CGPointMake(-1, -1);
    NSValue *value3 = [NSValue valueWithCGPoint:c];
    CGPoint d = CGPointMake(-1, 2);
    NSValue *value4 = [NSValue valueWithCGPoint:d];
    CGPoint e = CGPointMake(0, 2);
    NSValue *value5 = [NSValue valueWithCGPoint:e];
    CGPoint f = CGPointMake(0, 1);
    NSValue *value6 = [NSValue valueWithCGPoint:f];
    CGPoint g = CGPointMake(1, 1);
    NSValue *value7 = [NSValue valueWithCGPoint:g];
    CGPoint h = CGPointMake(1, 2);
    NSValue *value8 = [NSValue valueWithCGPoint:h];

    NSArray *vs = @[value1, value2, value3, value4, value5, value6, value7, value8];

    NSMutableArray *result = [NSMutableArray array];
    
    result = [Triangulate Process:vs];
    
    for (int i = 0; i < result.count/3; i++) {

        CGPoint p1 = [result[i*3+0] CGPointValue];
        CGPoint p2 = [result[i*3+1] CGPointValue];
        CGPoint p3 = [result[i*3+2] CGPointValue];
        
        NSLog(@"Triangle %d => (%0.0f, %0.0f) (%0.0f, %0.0f) (%0.0f, %0.0f)\n", i+1, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
    }
}

6.結(jié)果


測(cè)試結(jié)果.png

參考:
http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容