2012-07-07 3 views
6

이것은 iPad 응용 프로그램 용이지만 본질적으로 수학 문제입니다.베 지어 곡선을 사용하여 나선형 그리기

다양한 (단조 증가하는) 선폭의 원호를 그릴 필요가 있습니다. 커브가 시작될 때 시작 두께 (2pts라고 가정 해 봅시다)를 가지면 두께가 가장 두꺼운 호 끝까지 부드럽게 증가합니다 (12pt라고합시다).

UIBezierPath를 만들고 모양을 채우는 것이 가장 좋은 방법입니다. 첫 번째 시도는 두 개의 원호 (오프셋 중심 포함)를 사용하는 것이었고 90 °까지 잘 작동했지만 아크는 종종 90 °와 180 ° 사이에있게되어 접근 방법이 그것을 잘라 내지 못합니다.

example of 90 degree arc with increasing thickness

내 현재의 접근 방식은 베 지어 쿼드 또는 입방 곡선을 사용하여 (하나 약간 원호에서 성장과 하나가 약간 축소) 약간의 나선을 만드는 것입니다. 질문은 입니다. 제어점을 어디에 놓아야합니까? 원호로부터의 편차 (일명 "두께"모양)가 원하는 값입니다.

구속 :

  • 형상은 임의의 각도에서 시작 및 종료 할 수 있어야
  • 형상의 "두께"(원 편차)해야한다 (각각 180 ° 이내) 시작
  • 은 "두께"단조 증가해야 지정된 값으로 종료 (이 커질 수없고 다음 다시 작은)
  • 그것은 눈에 부드러운보고있다, 날카로운 굴절
  • 이 될 수 없다

다른 솔루션에도 열려 있습니다.

답변

5

내 접근 방식은 2 개 원호를 구성하고 그 사이에이 지역을 채 웁니다. 까다로운 점은이 호의 중심과 반지름을 알아내는 것입니다. 두께가 너무 크지 않다면 아주 좋아 보입니다. (잘라 내기 및 붙여 넣기 및 필요에 따라 직접 결정하십시오.) 클리핑 패스를 사용하여 향상시킬 수 있습니다.

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGMutablePathRef path = CGPathCreateMutable(); 

    // As appropriate for iOS, the code below assumes a coordinate system with 
    // the x-axis pointing to the right and the y-axis pointing down (flipped from the standard Cartesian convention). 
    // Therefore, 0 degrees = East, 90 degrees = South, 180 degrees = West, 
    // -90 degrees = 270 degrees = North (once again, flipped from the standard Cartesian convention). 
    CGFloat startingAngle = 90.0; // South 
    CGFloat endingAngle = -45.0; // North-East 
    BOOL weGoFromTheStartingAngleToTheEndingAngleInACounterClockwiseDirection = YES; // change this to NO if necessary 

    CGFloat startingThickness = 2.0; 
    CGFloat endingThickness = 12.0; 

    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 
    CGFloat meanRadius = 0.9 * fminf(self.bounds.size.width/2.0, self.bounds.size.height/2.0); 

    // the parameters above should be supplied by the user 
    // the parameters below are derived from the parameters supplied above 

    CGFloat deltaAngle = fabsf(endingAngle - startingAngle); 

    // projectedEndingThickness is the ending thickness we would have if the two arcs 
    // subtended an angle of 180 degrees at their respective centers instead of deltaAngle 
    CGFloat projectedEndingThickness = startingThickness + (endingThickness - startingThickness) * (180.0/deltaAngle); 

    CGFloat centerOffset = (projectedEndingThickness - startingThickness)/4.0; 
    CGPoint centerForInnerArc = CGPointMake(center.x + centerOffset * cos(startingAngle * M_PI/180.0), 
              center.y + centerOffset * sin(startingAngle * M_PI/180.0)); 
    CGPoint centerForOuterArc = CGPointMake(center.x - centerOffset * cos(startingAngle * M_PI/180.0), 
              center.y - centerOffset * sin(startingAngle * M_PI/180.0)); 

    CGFloat radiusForInnerArc = meanRadius - (startingThickness + projectedEndingThickness)/4.0; 
    CGFloat radiusForOuterArc = meanRadius + (startingThickness + projectedEndingThickness)/4.0; 

    CGPathAddArc(path, 
       NULL, 
       centerForInnerArc.x, 
       centerForInnerArc.y, 
       radiusForInnerArc, 
       endingAngle * (M_PI/180.0), 
       startingAngle * (M_PI/180.0), 
       !weGoFromTheStartingAngleToTheEndingAngleInACounterClockwiseDirection 
       ); 

    CGPathAddArc(path, 
       NULL, 
       centerForOuterArc.x, 
       centerForOuterArc.y, 
       radiusForOuterArc, 
       startingAngle * (M_PI/180.0), 
       endingAngle * (M_PI/180.0), 
       weGoFromTheStartingAngleToTheEndingAngleInACounterClockwiseDirection 
       ); 

    CGContextAddPath(context, path); 

    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor); 
    CGContextFillPath(context); 

    CGPathRelease(path); 
} 
+0

이것은 실제로 정말 멋지다! 당신은 나를 꽤 많이 구해 줬습니다. 이것은 내가 작업하고있는 접근 방식보다 훨씬 간단합니다 (나선형에 대한 베 지어 다항식을 풀어 냄). 나는 90 °의 배수로 작동하지만, 임의의 각도는 고통이 될 것입니다. 이것은 훨씬 더 좋습니다 ... –

+0

@JonHull 당신이 좋아하는 것을 기쁘게 생각합니다. 방금 "endingThickness> = startingThickness'라고 암묵적으로 가정 했음에도 불구하고이 조건이 만족되도록 입력 매개 변수를 쉽게 정렬 할 수 있어야합니다. 그렇지 않다면,'projectedEndingThickness'가 음수이고, 더 이상 대수학에 대해 확신 할 수없는 시나리오가있을 수 있습니다. 여전히 작동 할 수도 있지만 테스트하지는 못했습니다. – inwit

+0

오, 위대한 일하는 형제 님, 당신은 진정한 생명의 은인입니다., 감사합니다. – Dhiru

1

하나의 해결책은 수동으로 폴리 라인을 생성하는 것일 수 있습니다. 이것은 간단하지만 컨트롤이 고해상도로 표시되는 경우 생성하는 포인트의 크기를 확장해야한다는 단점이 있습니다. 나는 당신에게 아이폰 OS/ObjC 샘플 코드를 제공하는 아이폰 OS에 대해 충분히 잘 모르겠지만, 여기에 몇 가지 파이썬 틱 의사 코드는 다음과 같습니다

# lower: the starting angle 
# upper: the ending angle 
# radius: the radius of the circle 

# we'll fill these with polar coordinates and transform later 
innerSidePoints = [] 
outerSidePoints = [] 

widthStep = maxWidth/(upper - lower) 
width = 0 

# could use a finer step if needed 
for angle in range(lower, upper): 
    innerSidePoints.append(angle, radius - (width/2)) 
    outerSidePoints.append(angle, radius + (width/2)) 
    width += widthStep 

# now we have to flip one of the arrays and join them to make 
# a continuous path. We could have built one of the arrays backwards 
# from the beginning to avoid this. 

outerSidePoints.reverse() 
allPoints = innerSidePoints + outerSidePoints # array concatenation 

xyPoints = polarToRectangular(allPoints) # if needed 
+0

의사 코드를 보내 주셔서 감사합니다. 베 지어 커브 또는 호를 사용하는 솔루션을 찾을 수없는 경우 이것이 내 백업입니다. –

관련 문제