使用贝塞尔曲线绘制螺旋

这是一个iPad应用程序,但它本质上是一个数学问题。

我需要绘制一条不同(单调递增)线宽的圆弧。 在曲线开始时,它会有一个起始厚度(比如说2pts),然后厚度会平滑增加,直到弧线达到最大厚度(比如12pts)。

我想通过创建一个UIBezierPath并填充形状来达到此目的的最佳方式。 我的第一个尝试是使用两个圆弧(有偏移中心),并且工作精度可达90°,但弧度通常在90°和180°之间,所以这种方法不会削减它。

厚度增加的90度弧的例子

我目前的做法是使用贝塞尔四边形或三次曲线稍作旋转(一个从圆弧稍微增长,一个稍微缩小)。 问题是我在哪里放置控制点,以便与圆弧的偏差(也就是形状“厚度”)是我想要的值。

约束:

  • 形状必须能够以任意角度开始和结束(彼此相差180°以内)
  • 形状的“厚度”(与圆的偏差)必须以给定值开始和结束
  • “厚度”必须单调增加(它不能变大再变小)
  • 它必须看起来光滑,不能有任何尖锐的弯曲
  • 我也接受其他解决方案。


    我的方法是构造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);  
    }
    

    一种解决方案可能是手动生成折线。 这很简单,但它的缺点是如果控件以高分辨率显示,则必须放大生成的点的数量。 我不太了解iOS给你iOS / ObjC示例代码,但这里有一些python-ish伪代码:

    # 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
    
    链接地址: http://www.djcxy.com/p/61683.html

    上一篇: Using a Bezier Curve to draw a spiral

    下一篇: Draw ellipse with start and end angle in Objective