5

In Pygame, how can I calculate the coordinates for the three points of the head of an arrow, given a start point and an end point for the arrow, so that the arrowhead points in the same direction as the line?

def __draw_arrow(self, screen, colour, start, end):     
    start = self.__coordinate_lookup[start]
    end = self.__coordinate_lookup[end]
    dX = start[0] - end[0]
    dY = -(start[1] - end[1])
    print m.degrees(m.atan(dX/dY)) + 360 
    pygame.draw.line(screen,colour,start,end,2)

I've tried playing around with angles and with the gradient of the line, the fact the Y coordinates increase downwards instead of upwards is throwing me off and I would really appreciate a nudge in the right direction.

CFJ
  • 51
  • 1
  • 2
  • Possible duplicate of [Find coordinates to draw arrow head (isoscele triangle) at the end of a line](http://stackoverflow.com/questions/31462791/find-coordinates-to-draw-arrow-head-isoscele-triangle-at-the-end-of-a-line) – Spektre Apr 21 '17 at 08:34
  • appart of the duplicate QA you might want to see this too: [Apply Rotation to Cylinder based on Tube Ending Normal](http://stackoverflow.com/a/39674497/2521214) – Spektre Apr 21 '17 at 08:35

3 Answers3

4

This should work:

def draw_arrow(screen, colour, start, end):
    pygame.draw.line(screen,colour,start,end,2)
    rotation = math.degrees(math.atan2(start[1]-end[1], end[0]-start[0]))+90
    pygame.draw.polygon(screen, (255, 0, 0), ((end[0]+20*math.sin(math.radians(rotation)), end[1]+20*math.cos(math.radians(rotation))), (end[0]+20*math.sin(math.radians(rotation-120)), end[1]+20*math.cos(math.radians(rotation-120))), (end[0]+20*math.sin(math.radians(rotation+120)), end[1]+20*math.cos(math.radians(rotation+120)))))

Sorry for the poorly organized code. But as you said, the coordinates starting in the top-left do require some math to be flipped. Also, if you want to change the triangle from being equalatiral to something else you just have to change the rotation +/- 120 in line 4, or the 20* for a different radius.

Hope this helps :)

Vladimir Shevyakov
  • 2,511
  • 4
  • 19
  • 40
4

I denote start and end coordinates as startX, startY, endX, endY enter image description here

dX = endX - startX
dY = endY - startY

//vector length 
Len = Sqrt(dX* dX + dY * dY)  //use Hypot if available

//normalized direction vector components
udX = dX / Len
udY = dY / Len

//perpendicular vector
perpX = -udY
perpY = udX

//points forming arrowhead
//with length L and half-width H
arrowend  = (end) 

leftX = endX - L * udX + H * perpX
leftY = endY - L * udY + H * perpY

rightX = endX - L * udX - H * perpX
rightY = endY - L * udY - H * perpY
MBo
  • 77,366
  • 5
  • 53
  • 86
2

Vladimir's answer is great! For anyone visiting in the future, here's the function with control over every aspect of the arrow:

def arrow(screen, lcolor, tricolor, start, end, trirad):
    pg.draw.line(screen,lcolor,start,end,2)
    rotation = math.degrees(math.atan2(start[1]-end[1], end[0]-start[0]))+90
    pg.draw.polygon(screen, tricolor, ((end[0]+trirad*math.sin(math.radians(rotation)), end[1]+trirad*math.cos(math.radians(rotation))), (end[0]+trirad*math.sin(math.radians(rotation-120)), end[1]+trirad*math.cos(math.radians(rotation-120))), (end[0]+trirad*math.sin(math.radians(rotation+120)), end[1]+trirad*math.cos(math.radians(rotation+120)))))'
Alec
  • 8,529
  • 8
  • 37
  • 63