Question

I have a CGPath in some coordinate system that I'd like to draw. Doing so involves scaling the old coordinate system onto the Context's one. For that purpose, I use CGContextConcatCTM() which does transform all the points as it should. But, as it is a scaling operation, the horizontal/vertical line widths get changed to. E.g. a scale of 10 in x-direction, but of 1 in y-direction would lead to vertical lines being 10 times as thick as horizontal ones. Is there a way to keep the ease of use of translation matrices (e.g. CGAffineTransform) but not scaling line widths at the same time, e.g. a function like CGPathApplyAffineTransformToPoints?

Cheers

MrMage

Was it helpful?

Solution

You can use CGPathApply to iterate through the elements in a path. It's a little bit more complex than just a one-liner but if you package it up in a simple helper function, it might be useful for you. Here is one version that creates a new path and transforms it:

typedef struct {
    CGMutablePathRef path;
    CGAffineTransform transform;
} PathTransformInfo;

static void
PathTransformer(void *info, const CGPathElement *element)
{
    PathTransformInfo *transformerInfo = info;

    switch (element->type) {
        case kCGPathElementMoveToPoint:
            CGPathMoveToPoint(transformerInfo->path, &transformerInfo->transform,
                              element->points[0].x, element->points[0].y);
            break;

        case kCGPathElementAddLineToPoint:
            CGPathAddLineToPoint(transformerInfo->path, &transformerInfo->transform,
                                 element->points[0].x, element->points[0].y);
            break;

        case kCGPathElementAddQuadCurveToPoint:
            CGPathAddQuadCurveToPoint(transformerInfo->path, &transformerInfo->transform,
                                      element->points[0].x, element->points[0].y,
                                      element->points[1].x, element->points[1].y);
            break;

        case kCGPathElementAddCurveToPoint:
            CGPathAddCurveToPoint(transformerInfo->path, &transformerInfo->transform,
                                  element->points[0].x, element->points[0].y,
                                  element->points[1].x, element->points[1].y,
                                  element->points[2].x, element->points[2].y);
            break;
        case kCGPathElementCloseSubpath:
            CGPathCloseSubpath(transformerInfo->path);
            break;
    }
}

To use it you would do (this is the part I would put inside a helper function):

    PathTransformInfo info;
    info.path = CGPathCreateMutable();
    info.transform = CGAffineTransformMakeScale(2, 1);

    CGPathApply(originalPath, &info,  PathTransformer);

The transformed path is in info.path at this point.

OTHER TIPS

Do the transform when you add the path, but then remove the transform before you stroke the path. Instead of this:

CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 10, 10);   // scale path 10x

CGContextAddPath(ctx, somePath);

CGContextSetStrokeColorWithColor(ctx, someColor);
CGContextSetLineWidth(ctx, someWidth);   // uh-oh, line width is 10x, too
CGContextStrokePath(ctx);

CGContextRestoreGState(ctx);      // back to normal

Do this:

CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 10, 10);   // scale path 10x

CGContextAddPath(ctx, somePath);

CGContextRestoreGState(ctx);      // back to normal

CGContextSetStrokeColorWithColor(ctx, someColor);
CGContextSetLineWidth(ctx, someWidth);
CGContextStrokePath(ctx);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top