Drawing triangle/arrow on a line with CGContext

前端 未结 4 1208
南笙
南笙 2021-02-01 09:06

I am using the framework of route-me for working with locations. In this code the path between two markers(points) will be drawn as a line.

My Question: \"What code shou

相关标签:
4条回答
  • 2021-02-01 09:49

    I found this question as I had the same. I took drawnonward's example and it was so close... But with a flipping of cos and sin, I was able to get it to work:

    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;
    

    Once I did that, my arrows were pointed exactly the wrong way. So I added that second line (r += M_PI;)

    Thanks go to drawnonward!

    0 讨论(0)
  • 2021-02-01 09:53

    And here is Swift 4+ version for Friedhelm Brügge answer: (I'll draw it on image)

    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()
        }
    
    0 讨论(0)
  • 2021-02-01 09:57
    - (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);
    }
    
    0 讨论(0)
  • 2021-02-01 09:57

    The drawing of the actual triangle/arrow is easy once you have two points on your path.

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

    Getting the points is a little more tricky. You said path was a line, as opposed to a curve or series of curves. That makes it easier.

    Use CGPathApply to pick two points on the path. Probably, this is the last two points, one of which may be kCGPathElementMoveToPoint and the other will be kCGPathElementAddLineToPoint. Let mx,my be the first point and nx,ny be the second, so the arrow will point from m towards n.

    Assuming you want the arrow at the tip of the line, bx,by from above will equal nx,ny on the line. Choose a point dx,dy between mx,my and nx,ny to calculate the other points.

    Now calculate ax,ay and cx,cy such that they are on a line with dx,dy and equidistant from path. The following should be close, although I probably got some signs wrong:

    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;
    

    Length is the distance from the tip of the arrow to the base, and width is distance from the shaft to the barbs, or half the breadth of the arrow head.

    If path is a curve, then instead of finding mx,my as the previous point or move, it will be the final control point of the final curve. Each control point is on a line tangent to the curve and passing through the adjacent point.

    0 讨论(0)
提交回复
热议问题