I was in need of a little routine for drawing lines with arrows on them (and without using GDI+ which will of course do this natively) and so hacked up this little routine.
I know there are other routines for doing this in the forums, but this one doesn't use trig and allows for lines of any thickness and geometric style (dashed etc.) The routine is for Windows only as it requires that you select a gdi pen into the drawing DC before invoking the routine. This pen is used to render the line (not the arrowheads) and is how any style line is supported since you can use geometric pens etc.

Code: Select all
CompilerIf Defined(INCLUDE_ARROWEDLINES, #PB_Constant)=0
#INCLUDE_ARROWEDLINES=1
;/////////////////////////////////////////////////////////////////////////////////
;***Arrowed Lines***
;
;©nxSoftWare 2010.
;=================
; Stephen Rodriguez (srod)
; Created with Purebasic 4.51 for Windows.
;
; Platforms: Windows.
;
; Fully Unicode compliant and threadsafe.
;/////////////////////////////////////////////////////////////////////////////////
;-CONSTANTS.
;/////////////////////////////////////////////////////////////////////////////////
;The following constants are used for the arrowType parameter in the ArrowedLineXY function.
#ARROWEDLINES_FROMEND = 1
#ARROWEDLINES_TOEND = 2
#ARROWEDLINES_BOTHENDS = #ARROWEDLINES_FROMEND | #ARROWEDLINES_TOEND
;/////////////////////////////////////////////////////////////////////////////////
;-PUBLIC FUNCTIONS.
;/////////////////////////////////////////////////////////////////////////////////
;The following function draws an arrowed line. The line itself is drawn using the pen currently selected into the hdc.
;The arrowheads are drawn and filled with the specified color (they do not use the currently selected pen because for a filled arrow
;you really only get good results with a pen of thickness at most 1).
Procedure ArrowedLineXY(hdc, x1, y1, x2, y2, arrowLength, baseWidth, arrowType = #ARROWEDLINES_BOTHENDS, arrowColor = #Black)
Protected lineLength.d, ratio.d, left, top, right, bottom, blnSwitchedPts, aLeft, aTop, aRight, aBottom
Protected lambda.d, Dim vertices.POINT(2), i, brush, oldBrush, oldPen, t1
;Now check the parameters
If arrowLength > 0 And baseWidth > 0 And arrowType = arrowType & #ARROWEDLINES_BOTHENDS
lineLength = Sqr((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1))
If lineLength
t1 = lineLength
If arrowType & #ARROWEDLINES_FROMEND
t1 - arrowLength
EndIf
If arrowType & #ARROWEDLINES_TOEND
t1 - arrowLength
EndIf
If t1 >= 0
lambda = baseWidth / lineLength / 2
;Calculate the adjusted end-points.
ratio = arrowLength / lineLength
If x1 < x2 Or (x1 = x2 And y1 < y2)
left = x1 : top = y1 : right = x2 : bottom = y2
Else
left = x2 : top = y2 : right = x1 : bottom = y1
blnSwitchedPts = #True
EndIf
aLeft = left : aTop = top : aRight = right : aBottom = bottom
brush = CreateSolidBrush_(arrowColor)
If brush
oldBrush = SelectObject_(hdc, brush)
oldPen = SelectObject_(hdc, GetStockObject_(#NULL_PEN))
For i = #ARROWEDLINES_FROMEND To #ARROWEDLINES_TOEND
If arrowType & i
If (i = #ARROWEDLINES_FROMEND And blnSwitchedPts = #False) Or (i = #ARROWEDLINES_TOEND And blnSwitchedPts = #True)
aLeft = (1 - ratio) * left + ratio * right
aTop = (1 - ratio) * top + ratio * bottom
vertices(0)\x = left : vertices(0)\y = top
vertices(1)\x = aLeft - lambda * (bottom - top) : vertices(1)\y = aTop + lambda * (right - left)
vertices(2)\x = aLeft<<1 - vertices(1)\x : vertices(2)\y = aTop<<1 - vertices(1)\y
Else
aRight = (1 - ratio) * right + ratio * left
aBottom = (1 - ratio) * bottom + ratio * top
vertices(0)\x = right : vertices(0)\y = bottom
vertices(1)\x = aRight - lambda * (bottom - top) : vertices(1)\y = aBottom + lambda * (right - left)
vertices(2)\x = aRight<<1 - vertices(1)\x : vertices(2)\y = aBottom<<1 - vertices(1)\y
EndIf
Polygon_(hdc, vertices(), 3)
EndIf
Next
SelectObject_(hdc, oldBrush)
SelectObject_(hdc, oldPen)
DeleteObject_(brush)
EndIf
;Draw the main (truncated) line.
MoveToEx_(hdc, aLeft, aTop, 0)
LineTo_(hdc, aRight, aBottom)
Else
MoveToEx_(hdc, x1, y1, 0)
LineTo_(hdc, x2, y2)
EndIf
EndIf
Else ;Just draw the complete line.
MoveToEx_(hdc, x1, y1, 0)
LineTo_(hdc, x2, y2)
EndIf
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////
CompilerEndIf