Preserve line width while scaling all points in the context with CGAffineTransform

cgaffinetransform, cgcontext, cgpath, core-graphics, stroke

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.

Problem

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

Original source