Рисование треугольника / стрелки на линии с помощью CGContext - PullRequest
11 голосов
/ 23 марта 2010

Я использую каркас route-me для работы с локациями. В этом коде путь между двумя маркерами (точками) будет нарисован в виде линии.

Мой вопрос: «Какой код мне добавить, если я хочу добавить стрелку в середине (или вверху) строки, чтобы она указывала направление»

Спасибо



- (void)drawInContext:(CGContextRef)theContext
{
    renderedScale = [contents metersPerPixel];

    float scale = 1.0f / [contents metersPerPixel];

    float scaledLineWidth = lineWidth;
    if(!scaleLineWidth) {
        scaledLineWidth *= renderedScale;
    }
    //NSLog(@"line width = %f, content scale = %f", scaledLineWidth, renderedScale);

    CGContextScaleCTM(theContext, scale, scale);

    CGContextBeginPath(theContext);
    CGContextAddPath(theContext, path);

    CGContextSetLineWidth(theContext, scaledLineWidth);
    CGContextSetStrokeColorWithColor(theContext, [lineColor CGColor]);
    CGContextSetFillColorWithColor(theContext, [fillColor CGColor]);

    // according to Apple's documentation, DrawPath closes the path if it's a filled style, so a call to ClosePath isn't necessary
    CGContextDrawPath(theContext, drawingMode);
}

Ответы [ 4 ]

20 голосов
/ 09 октября 2011
- (void) drawLine: (CGContextRef) context from: (CGPoint) from to: (CGPoint) to 
{
    double slopy, cosy, siny;
    // Arrow size
    double length = 10.0;  
    double width = 5.0;

    slopy = atan2((from.y - to.y), (from.x - to.x));
    cosy = cos(slopy);
    siny = sin(slopy);

    //draw a line between the 2 endpoint
    CGContextMoveToPoint(context, from.x - length * cosy, from.y - length * siny );
    CGContextAddLineToPoint(context, to.x + length * cosy, to.y + length * siny);
    //paints a line along the current path
    CGContextStrokePath(context);

    //here is the tough part - actually drawing the arrows
    //a total of 6 lines drawn to make the arrow shape
    CGContextMoveToPoint(context, from.x, from.y);
    CGContextAddLineToPoint(context,
                        from.x + ( - length * cosy - ( width / 2.0 * siny )),
                        from.y + ( - length * siny + ( width / 2.0 * cosy )));
    CGContextAddLineToPoint(context,
                        from.x + (- length * cosy + ( width / 2.0 * siny )),
                        from.y - (width / 2.0 * cosy + length * siny ) );
    CGContextClosePath(context);
    CGContextStrokePath(context);

    /*/-------------similarly the the other end-------------/*/
    CGContextMoveToPoint(context, to.x, to.y);
    CGContextAddLineToPoint(context,
                        to.x +  (length * cosy - ( width / 2.0 * siny )),
                        to.y +  (length * siny + ( width / 2.0 * cosy )) );
    CGContextAddLineToPoint(context,
                        to.x +  (length * cosy + width / 2.0 * siny),
                        to.y -  (width / 2.0 * cosy - length * siny) );
    CGContextClosePath(context);
    CGContextStrokePath(context);
}
6 голосов
/ 05 апреля 2010

Рисование фактического треугольника / стрелки легко, если у вас есть две точки на вашем пути.

CGContextMoveToPoint( context , ax , ay );
CGContextAddLineToPoint( context , bx , by );
CGContextAddLineToPoint( context , cx , cy );
CGContextClosePath( context ); // for triangle

Получить очки немного сложнее. Вы сказали, что путь - это линия, а не кривая или серия кривых. Это облегчает.

Используйте CGPathApply, чтобы выбрать две точки на пути. Вероятно, это последние две точки, одна из которых может быть kCGPathElementMoveToPoint, а другая - kCGPathElementAddLineToPoint. Пусть mx, my будет первой точкой, а nx, ny - второй, поэтому стрелка будет указывать от m к n.

Если вы хотите, чтобы стрелка на конце линии, bx, сверху была равна nx, ny на линии. Выберите точку dx, dy между mx, my и nx, ny, чтобы вычислить другие точки.

Теперь вычислите ax, ay и cx, cy так, чтобы они были на линии с dx, dy и равноудалены от пути. Следующее должно быть близко, хотя я, вероятно, неправильно понял некоторые признаки:

r = atan2( ny - my , nx - mx );
bx = nx;
by = ny;
dx = bx + sin( r ) * length;
dy = by + cos( r ) * length;
r += M_PI_2; // perpendicular to path
ax = dx + sin( r ) * width;
ay = dy + cos( r ) * width;
cx = dx - sin( r ) * width;
cy = dy - cos( r ) * width;

Длина - это расстояние от вершины стрелки до основания, а ширина - это расстояние от оси до зубцов, или половина ширины головки стрелки.

Если путь - это кривая, то вместо нахождения mx, my в качестве предыдущей точки или движения, она будет конечной контрольной точкой последней кривой. Каждая контрольная точка находится на линии, касательной к кривой и проходящей через соседнюю точку.

2 голосов
/ 16 апреля 2010

Я нашел этот вопрос, поскольку у меня было то же самое. Я взял пример обращается к нему, и он был так близок ... Но, взглянув на кос и грех, я смог заставить его работать:

r = atan2( ny - my , nx - mx );
r += M_PI;
bx = nx;
by = ny;
dx = bx + cos( r ) * length;
dy = by + sin( r ) * length;
r += M_PI_2; // perpendicular to path
ax = dx + cos( r ) * width;
ay = dy + sin( r ) * width;
cx = dx - cos( r ) * width;
cy = dy - sin( r ) * width;

Как только я это сделал, мои стрелки были направлены совершенно неверно. Поэтому я добавил эту вторую строку (r += M_PI;)

Спасибо, обращайтесь к Drawonward!

0 голосов
/ 02 января 2019

А вот Swift 4 + версия для Фридхельм Брюгге ответ: (я нарисую это на изображении)

func drawArrow(image: UIImage, ptSrc: CGPoint, ptDest: CGPoint) {

        // create context with image size
        UIGraphicsBeginImageContext(image.size)
        let context = UIGraphicsGetCurrentContext()

        // draw current image to the context
        image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))

        var slopY: CGFloat, cosY: CGFloat, sinY: CGFloat;
        // Arrow size
        let length: CGFloat = 35.0;
        let width: CGFloat = 35.0;

        slopY = atan2((ptSrc.y - ptDest.y), (ptSrc.x - ptDest.x));
        cosY = cos(slopY);
        sinY = sin(slopY);

        //here is the tough part - actually drawing the arrows
        //a total of 6 lines drawn to make the arrow shape
        context?.setFillColor(UIColor.white.cgColor)
        context?.move(to: CGPoint(x: ptSrc.x, y: ptSrc.y))
        context?.addLine(to: CGPoint(x: ptSrc.x + ( -length * cosY - ( width / 2.0 * sinY )), y: ptSrc.y + ( -length * sinY + ( width / 2.0 * cosY ))))
        context?.addLine(to: CGPoint(x: ptSrc.x + (-length * cosY + ( width / 2.0 * sinY )), y: ptSrc.y - (width / 2.0 * cosY + length * sinY )))
        context?.closePath()
        context?.fillPath()

        context?.move(to: CGPoint(x: ptSrc.x, y: ptSrc.y))
        context?.addLine(to: CGPoint(x: ptDest.x +  (length * cosY - ( width / 2.0 * sinY )), y: ptDest.y +  (length * sinY + ( width / 2.0 * cosY ))))
        context?.addLine(to: CGPoint(x: ptDest.x +  (length * cosY + width / 2.0 * sinY), y: ptDest.y -  (width / 2.0 * cosY - length * sinY)))
        context?.closePath()
        context?.fillPath()

        // draw current context to image view
        imgView.image = UIGraphicsGetImageFromCurrentImageContext()

        //close context
        UIGraphicsEndImageContext()
    }
...