4

-bezierPathByFlatteningPath簡単に言えば、iOS で使用できるNSBezierPath に相当するものを探しています。これが CGPath を直接処理する関数であるか、UIBezierPath のメソッドであるかは問題ではありません。この 2 つは簡単に相互に変換できるからです。CGPath リファレンスUIBezierPath クラス リファレンスも、そのような関数やメソッドの存在を示していません。

また、私は CGPath のCGPathApply機能を認識しており、パスの要素をCGPathApplierFunction. これに対する既存の解​​決策を探しています。applier 関数、UIBezierPath のカテゴリなどです。確かに存在します。

4

2 に答える 2

7

Erica Sadun は、UIBezierPath と CGPathRef を処理するための一連の便利な関数を提供しています。

このコードは本書で使用されています。

彼女は CGPathRef の平坦化の実装を提供していませんが、 https ://github.com/erica/iOS-Drawing/blob/master/C05/Quartz%20Book%20Pack にある関数を使用して簡単に実行できます。 /Bezier/BezierFunctions.m

特に、これらの関数は、非線形ベジエ セグメントを離散化するのに役立ちます。

float CubicBezier(float t, float start, float c1, float c2, float end)
float QuadBezier(float t, float start, float c1, float end)
CGPoint CubicBezierPoint(CGFloat t, CGPoint start, CGPoint c1, CGPoint c2, CGPoint end);
CGPoint QuadBezierPoint(CGFloat t, CGPoint start, CGPoint c1, CGPoint end);

したがって、基本的には、空の CGMutablePathRef を初期化し、元のパスの各 CGPath 要素について、線形の場合はコピーするか、ベジエ セグメントの次数に従って離散化します。

Ramer–Douglas–Peucker アルゴリズムを適用して不要な点を削除することもできます。

次を直接使用することもできます。- (NSArray *) interpolatedPathPointsこれは、パスの近似を構築するために使用できるポイントの NSArray を返します。このアルゴリズムは単純なので、次のような場合は結果を単純化する必要があります。前と同じように、Ramer–Douglas–Peucker アルゴリズムがその役割を果たします。

実際の離散化は次のようになります。コードは自己完結型ではないため、すべての依存関係を使用する必要があります。

- (NSArray *) interpolatedPathPoints
{
    NSMutableArray *points = [NSMutableArray array];
    BezierElement *current = nil;
    int overkill = 3;
    for (BezierElement *element in self.elements)
    {
        switch (element.elementType)
        {
            case kCGPathElementMoveToPoint:
            case kCGPathElementAddLineToPoint:
                [points addObject:[NSValue valueWithCGPoint:element.point]];
                current = element;
                break;
            case kCGPathElementCloseSubpath:
                current = nil;
                break;
            case kCGPathElementAddCurveToPoint:
            {
                for (int i = 1; i < NUMBER_OF_BEZIER_SAMPLES * overkill; i++)
                {
                    CGFloat percent = (CGFloat) i / (CGFloat) (NUMBER_OF_BEZIER_SAMPLES * overkill);
                    CGPoint p = CubicBezierPoint(percent, current.point, element.controlPoint1, element.controlPoint2, element.point);
                    [points addObject:[NSValue valueWithCGPoint:p]];
                }
                [points addObject:[NSValue valueWithCGPoint:element.point]];
                current = element;
                break;
            }
            case kCGPathElementAddQuadCurveToPoint:
            {
                for (int i = 1; i < NUMBER_OF_BEZIER_SAMPLES * overkill; i++)
                {
                    CGFloat percent = (CGFloat) i / (CGFloat) (NUMBER_OF_BEZIER_SAMPLES * overkill);
                    CGPoint p = QuadBezierPoint(percent, current.point, element.controlPoint1, element.point);
                    [points addObject:[NSValue valueWithCGPoint:p]];
                }
                [points addObject:[NSValue valueWithCGPoint:element.point]];
                current = element;
                break;
            }
        }
    }
    return points;
}

コードはエリカ・サドゥンのものです。完全な実装については、https ://github.com/erica/iOS-Drawing を参照してください。

Rob Napier は、 iOS 6 Pushing the limitsの Chapter 26 Fancy Text Layoutでベジエ曲線についても書いています。彼は完全な UIBezierPath を平坦化しようとしているのではなく、4 つのポイントで定義された 1 つのキュービック ベジェ パスのみをフラット化しようとしていましたが、実際にはまったく同じことです (ベジェ パスの離散化) 。高速ベジェ

于 2014-03-28T16:49:56.233 に答える
1

基本的にやろうとしているのは、パスを変換して、すべての曲線と quadCurves を線に置き換えることです。これを非常に簡単に行うことができ、次CGPathApplyのカテゴリにラップできUIBezierPathます。

#import <CoreGraphics/CoreGraphics.h>

@interface UIBezierPath (Flatten)
- (UIBezierPath *)bezierPathByFlatteningPath;
@end

void __flattenBezierPath(void *target, const CGPathElement *element) {
    UIBezierPath *newPath = (__bridge UIBezierPath *)(target);

    switch (element->type) {
        case kCGPathElementMoveToPoint:
            [newPath moveToPoint:element->points[0]];
            break;

        case kCGPathElementAddLineToPoint:
            [newPath addLineToPoint:element->points[0]];
            break;

        case kCGPathElementCloseSubpath:
            [newPath closePath];
            break;

        case kCGPathElementAddCurveToPoint:
            [newPath addLineToPoint:element->points[2]];
            break;

        case kCGPathElementAddQuadCurveToPoint:
            [newPath addLineToPoint:element->points[1]];
            break;
    }

}

@implementation UIBezierPath (Flatten)

- (UIBezierPath *)bezierPathByFlatteningPath
{
    UIBezierPath *newPath = [UIBezierPath bezierPath];
    CGPathApply(self.CGPath, (__bridge void *)(newPath), __flattenBezierPath);
    return newPath;
}

@end

以下に効果を示します。緑が元のパス、赤がこのメソッドによって生成されたパス、黄色が使用したパスです。setFlatness:MAXFLOAT

ベジエ パスのさまざまなレンダリング

于 2013-09-18T05:53:59.367 に答える