8

나는 라인과 텍스트를 형성하는 점의 배열을 가지고있다. 이 선을 따라 텍스트 그리기에 대해 어떻게 할 수 있습니까?QuartzCore에서 경로를 따라 텍스트 그리기

- (void)drawRect:(CGRect)rect 

의 UIView?

문제없이 경로를 그릴 수 있습니다. 간과 한 표준 방법이나 그 경로를 따라 텍스트를 그릴 수있는 프레임 워크가 있습니까? 이상적으로 QuartzCore/CoreGraphics만을 사용하여 이것을하고 싶습니다.

각 문자의 너비를 계산하고 모든 문자를 회전 해 보았습니다. 이런 종류의 작품이지만 더 우아한 방법이 있는지 궁금해하고있었습니다.

+0

경로에서 각 문자의 수동 위치 지정 및 회전을 언급했습니다. – Dimitris

+0

라인이 직선 인 경우 회전 및 평행 이동 CTM을 적용하면됩니다. – kennytm

+0

@KennyTM 경로는이 경로의 하나 또는 그 이상의 세그먼트를 다루는 텍스트가있는 여러 개의 직선으로 구성됩니다. – FelixLam

답변

11

Mac OS X에서이 작업을 수행 할 수 있다고 믿지만, 가장 가까운 아이폰은 CGContextShowGlyphsWithAdvances이고이 작업은 회전되지 않습니다.

루프를 사용하고 각 문자를 다음과 같이 사용하여 그리는 것이 너무 어렵지 않아야합니다. 이는 애플의 문서에서 적응 및 테스트하지, 그래서 조심한다 :

CGContextSelectFont(myContext, "Helvetica", 12, kCGEncodingMacRoman); 
CGContextSetCharacterSpacing(myContext, 10); 
CGContextSetTextDrawingMode(myContext, kCGTextFillStroke); 
CGContextSetRGBFillColor(myContext, 0, 0, 0, 1); 
CGContextSetRGBStrokeColor(myContext, 0, 0, 0, 1); 

NSUInteger charIndex = 0; 
for(NSString *myChar in arrayOfChars) { 
    char *cString = [myChar UTF8String]; 
    CGPoint charOrigin = originForPositionAlongPath(charIndex, myPath); 
    CGFloat slope = slopeForPositionAlongPath(charIndex, myPath); 

    CGContextSetTextMatrix(myContext, CGAffineTransformMakeRotation(slope)); 
    CGContextShowTextAtPoint(myContext, charOrigin.x, charOrigin.y, cString, 1); 
} 

편집 : 다음은 PositionAlongPath 기능의 생각이다. 다시 말하지만, 테스트를 거치지는 않았지만 가까이 있어야합니다. originAlong ... 경로가 부족하면 (-1, -1)을 반환합니다.

CGPoint originForPositionAlongPath(int index, NSArray *path) { 
    CGFloat charWidth = 12.0; 
    CGFloat widthToGo = charWidth * index; 

    NSInteger i = 0; 
    CGPoint position = [path objectAtIndex:i]; 

    while(widthToGo >= 0) { 
      //out of path, return invalid point 
     if(i >= [path count]) { 
      return CGPointMake(-1, -1); 
     } 

     CGPoint next = [path objectAtIndex:i+1]; 

     CGFloat xDiff = next.x - position.x; 
     CGFloat yDiff = next.y - position.y; 
     CGFloat distToNext = sqrt(xDiff*xDiff + yDiff*yDiff); 

     CGFloat fracToGo = widthToGo/distToNext 
      //point is before next point in path, interpolate the answer 
     if(fracToGo < 1) { 
      return CGPointMake(position.x+(xDiff*fracToGo), position.y+(yDiff*fracToGo)); 
     } 

      //advance to next point on the path 
     widthToGo -= distToNext; 
     position = next; 
     ++i; 
    } 
} 


CGFloat slopeForPositionAlongPath(int index, NSArray *path) { 
    CGPoint begin = originForPositionAlongPath(index, path); 
    CGPoint end = originForPositionAlongPath(index+1, path); 

    CGFloat xDiff = end.x - begin.x; 
    CGFloat yDiff = end.y - begin.y; 

    return atan(yDiff/xDiff); 
} 
+0

어려운 부분은 originForPositionAlongPath, slopeForPositionAlongPath의 두 함수를 찾는 것입니다. – FelixLam

+0

당신이 시도한 것이 내가 추가 한 것과 그렇지 않은 것의 라인을 따르고 있는지 확실하지 않습니다. 당신이 시도한 것이 왜 충분하지 않은지에 대한 아이디어를 나에게 주면, 나는 내가 생각한 것을 향상시킬 수 있는지 알게 될 것입니다. –

+0

이전에 두 개의 세그먼트가있는 주어진 간단한 경로에 대해서만 테스트 케이스를 구현했지만 작성한 두 가지 일반적인 방법을 공식화했습니다. 오늘 나중에 확인해 보겠습니다. 당신의 도움에 감사드립니다. 나는 더 많은 피드백을 가지고 돌아올 것이다. – FelixLam

1

이 아마 무관하지만 당신은 SVG (http://www.w3.org/TR/SVG/text.html#TextOnAPath)와 경로를 따라 텍스트 수 있으며, iPhoneOS 그것을 지원합니다.

+0

감사합니다.하지만 QuartzCore에서이 작업을 수행 할 수 있기를 원하는데 정확히 목표가 아닙니다. – FelixLam

0

위의 예는 불행히도 예상대로 작동하지 않았습니다. 이제 경로를 따라 텍스트를 그리는 올바른 방법을 찾았습니다.

여기에 우리가 간다 : 등 (1)의 내 응용 프로그램에서 발췌,하지만 난 어떤 의견 분명한 일을 할 것입니다 :

당신이 코드 일을 할 수 없다.

// MODIFIED ORIGIN FUNCTION 

CGPoint originForPositionAlongPath(float *l, float nextW, int index, NSArray *path) { 
CGFloat widthToGo = *l + nextW; 


NSInteger i = 0; 
CGPoint position = [[path objectAtIndex:i] CGPointValue]; 

while(widthToGo >= 0) { 
    //out of path, return invalid point 
    if(i+1 >= [path count]) { 
     return CGPointMake(-1, -1); 
    } 

    CGPoint next = [[path objectAtIndex:i+1] CGPointValue]; 

    CGFloat xDiff = next.x - position.x; 
    CGFloat yDiff = next.y - position.y; 
    CGFloat distToNext = sqrt(xDiff*xDiff + yDiff*yDiff); 

    CGFloat fracToGo = widthToGo/distToNext; 
    //point is before next point in path, interpolate the answer 
    if(fracToGo < 1) { 
     return CGPointMake(position.x+(xDiff*fracToGo), position.y+(yDiff*fracToGo)); 
    } 

    //advance to next point on the path 
    widthToGo -= distToNext; 
    position = next; 
    ++i; 
} 
} 

// MODIFIED SLOPE FUNCTION 

CGFloat slopeForPositionAlongPath(float* l, float nextW, int index, NSArray *path) { 
CGPoint begin = originForPositionAlongPath(l, 0, index, path); 
CGPoint end = originForPositionAlongPath(l, nextW, index+1, path); 

CGFloat xDiff = end.x - begin.x; 
CGFloat yDiff = end.y - begin.y; 

return atan(yDiff/xDiff); 
} 


// IMPORTANT: CHARACTER WIDTHS FOR HELVETICA, ABOVE EXAMPLE USES FIXED WIDTHS WHICH IS NOT ACCURATE 

float arraychars[] = { 
0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  
0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.278, 
0.333, 0.333, 0.389, 0.584, 0.278, 0.584, 0.278, 0.278, 
0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 
0.556, 0.556, 0.333, 0.333, 0.584, 0.584, 0.584, 0.611, 
0.975, 0.722, 0.722, 0.722, 0.722, 0.667, 0.611, 0.778, 
0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778, 
0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, 
0.667, 0.667, 0.611, 0.333, 0.278, 0.333, 0.584, 0.556, 
0.278, 0.556, 0.611, 0.556, 0.611, 0.556, 0.333, 0.611, 
0.611, 0.278, 0.278, 0.556, 0.278, 0.889, 0.611, 0.611, 
0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778, 
0.556, 0.556, 0.5, 0.389, 0.28, 0.389, 0.584, 0,  
0,  0,  0,  0,  0,  0,  0,  0,  
0,  0,  0,  0,  0,  0,  0,  0,  
0.278, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 
0.333, 0,  0.333, 0.333, 0,  0.333, 0.333, 0.333, 
0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.28, 0.556, 
0.333, 0.737, 0.37, 0.556, 0.584, 0.333, 0.737, 0.333, 
0.4, 0.584, 0.333, 0.333, 0.333, 0.611, 0.556, 0.278, 
0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611, 
0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1,  0.722, 
0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, 
0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584, 
0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611, 
0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.556, 
0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, 
0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, 
0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556, 
       };  

void o_DrawContourLabel(void *myObjectInstance, TransBuffer* transBuffer,  MapPainterIphone* mp,const Projection& projection, 
        const MapParameter& parameter, 
        const LabelStyle& style, 
        const std::string& text, 
        size_t transStart, size_t transEnd){ 
// HERE WE Initialize the font etc. 

CGContextSelectFont(context, "Helvetica-Bold", 10, kCGEncodingMacRoman); 
CGContextSetCharacterSpacing(context, 0); 
CGContextSetTextDrawingMode(context, kCGTextFillStroke); 
CGContextSetLineWidth(context, 3.0); 
CGContextSetRGBFillColor(context, style.GetTextR(), style.GetTextG(), style.GetTextB(), style.GetTextA()); 
CGContextSetRGBStrokeColor(context, 1, 1, 1, 1); 

// Here we prepare a NSArray holding all waypoints of our path. 
// I fill it from a "transBuffer" but you may fill it with whatever you want 

NSMutableArray* path = [[NSMutableArray alloc] init]; 
if (transBuffer->buffer[transStart].x<transBuffer->buffer[transEnd].x) { 
    for (size_t j=transStart; j<=transEnd; j++) { 
     if (j==transStart) { 
      [path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[j].x,transBuffer->buffer[j].y)]]; 
     } 
     else { 
      [path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[j].x,transBuffer->buffer[j].y)]]; 
     } 
    } 
} 
else { 
    for (size_t j=0; j<=transEnd-transStart; j++) { 
     size_t idx=transEnd-j; 

     if (j==0) { 
      [path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[idx].x,transBuffer->buffer[idx].y)]]; 

     } 
     else { 
      [path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[idx].x,transBuffer->buffer[idx].y)]]; 

     } 
    } 
} 

// if path is too short for "estimated text length" then exit 

if (pathLength(path)<text.length()*7) { 
    // Text is longer than path to draw on 
    return; 
} 

// NOW PAINT CHAR FOR CHAR USING CHARACTER WIDTHS TABLE 

float lenUpToNow = 0; 

CGAffineTransform transform=CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0); 
NSUInteger charIndex = 0; 
for(int i=0;i<text.length();i++) { 
    char *cString = (char*)malloc(2*sizeof(char)); 
    cString[0] = text.at(i); 
    cString[1]=0; 

    float nww = arraychars[cString[0]]*8*1.4; 

    CGPoint charOrigin = originForPositionAlongPath(&lenUpToNow, 0, charIndex, path); 
    CGFloat slope = slopeForPositionAlongPath(&lenUpToNow, nww, charIndex, path); 
    std::cout << " CHARACTER " << cString << " placed at " << charOrigin.x << "," << charOrigin.y << std::endl; 

    // DO NOT FORGET TO DO THIS (TWO TRANSFORMATIONS) .. one for the rotation 
    // and one for mirroring, otherwise the text will be mirrored due to a 
    // crappy coordinate system in QuartzCore. 

    CGAffineTransform ct = CGAffineTransformConcat(transform,CGAffineTransformMakeRotation(slope)); 
    CGContextSetTextMatrix(context, ct); 
    CGContextSetTextDrawingMode(context, kCGTextStroke); 
    CGContextShowTextAtPoint(context, charOrigin.x, charOrigin.y, cString, 1); 

    CGContextSetTextDrawingMode(context, kCGTextFill); 
    CGContextShowTextAtPoint(context, charOrigin.x, charOrigin.y, cString, 1); 
    std::cout << " ... len (" << (int)cString[0] << ") = " << arraychars[cString[0]] << " up to now: " << lenUpToNow << std::endl; 
    lenUpToNow += nww; 

    charIndex++; 
    free(cString); 
} 


} 
+1

다른 글꼴 (예 :'sizeWithFont :')을 사용하여 문자의 너비를 결정하기 위해 NSString 메소드를 사용하는 것으로 끝났습니다. 우리의 주된 문제점은'CGContextShowTextAtPoint'는 비 - 라틴 문자를 지원하지 않는다는 것입니다. – FelixLam

+0

나는 라틴어가 아닌 문자들과 같은 문제가 있었다. 다음 메서드를 사용하여 고정 : –

+0

NSString * strCC = [NSString stringWithUTF8String : label.text.c_str()]; const char * plCC = [strCC cStringUsingEncoding : NSMacOSRomanStringEncoding]; 그리고 나서 plCC를 그립니다 ... 매력처럼 작동합니다. –

관련 문제