Line with arrow head

Seems to be quite easy question but I have no idea.

I want to draw a line with an arrow head. However, I couldn’t find any method except below one.
g.Drawline

Can you let me know how to do it?

there is no “built-in” draw an arrow routine.

use 3 drawline commands. and a little geometry

I’ve been using the g.FillPolygon function to draw some simple triangles as part of some custom controls I’ve done on a Canvas.

I’m sure with a little patience and geometry, as Dave said, you could create a DrawArrow method that takes a few parameters, like g As Graphics, x1 As Int32, y1 As Int32, x2 As Int32, y2 As Int32, where g is a reference to your Graphics object in your Paint event, x1 & y1 is the “from” point of your arrow and x2 & y2 is the “to” point.

Making a separate DrawArrow() method could help with re-usability, if you need more than one arrow.

I could try to help with the geometry, but I’m afraid my head might explode in the process. I’m much more of a business logic guy than a graphics person.

I hope that helps.

Here is a method that draws a simple arrow with two lines for an arrow tip:

[code]Public Sub DrawArrow(g as Graphics, FromX as double, FromY as double, ToX as double, ToY as Double, AThickness as Integer, AColour as Color)
Dim HeadLength as Double = 10
Dim Pi as Double = 3.1415926

//Set the arrow properties
Dim Angle as Double = ATan2(ToY-FromY,ToX-FromX)
g.ForeColor = AColour
g.PenHeight = AThickness
g.PenWidth = AThickness
g.DrawLine(FromX, FromY,ToX, ToY)

//Add the two lines for the arrowhead
g.DrawLine(ToX, ToY, ToX-HeadLengthCos(Angle-Pi/7), ToY-HeadLengthSin(Angle-Pi/7))
g.DrawLine(ToX, ToY, ToX-HeadLengthCos(Angle+Pi/7), ToY-HeadLengthSin(Angle+Pi/7))
End Sub
[/code]

Here is the line of code that is added to the Canvas Paint event:

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint //Draw an arrow //g is graphics, FromX and FromY are the starting coordinates of the arrow //ToX and ToY are the arrow end points //AThickness is the arrow thickness //AColour is the arrow colour DrawArrow(g,5, 5, 100, 100, 2, &c11CCDD) End Sub

…and a screen grab of what the code makes…

Here’s another version that uses a filled polygon for the arrowhead.

[code]module EnhancedGraphics

Public Sub DrawArrow(extends g As Graphics, x1 As Double, y1 As Double, x2 As Double, y2 As Double)
'Draw a line with arrowhead at the end
'Calling parameters are the same as for DrawLine
'Arrowhead parameters are determined by properties arrowLength and arrowAngle
const deg2halfrad = 0.008726646259972
'Get angle of line
dim aLine As Double = ATan2(y2-y1,x2-x1)
'calculate angle of arrowhead lines
dim ahLineA As Double = aLine-mArrowAngledeg2halfrad
dim ahLineB As Double = aLine+arrowAngle
deg2halfrad
'calculate x and y coordinates of the arrowhead points
dim ahLineAx As Double = x2-mArrowLengthcos(ahLineA)
dim ahLineBx As Double = x2-mArrowLength
cos(ahLineB)
dim ahLineAy As Double = y2-mArrowLengthsin(ahLineA)
dim ahLineBy As Double = y2-mArrowLength
sin(ahLineB)
dim poly() As Double =Array(0,x2,y2,ahLineAx,ahLineAy,ahLineBx,ahLineBy)
'Draw the line
g.DrawLine(x1,y1,x2,y2)
'Draw the arrowhead outline
g.DrawPolygon(poly)
'Fill the arrowhead
g.FillPolygon(poly)
End Sub

Private Property marrowLength as Double = 25

Private Property marrowAngle as Double = 30

Public Property arrowAngle as Double
Get
Return marrowAngle
End Get

Set
marrowAngle = value
End Set
End Property

Public Property arrowLength as Double
Get
Return marrowLength
End Get

Set
marrowLength = value
End Set
End Property

End Module

'Test Paint Event
Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
g.ForeColor = &c000000
g.PenHeight = 1
g.PenWidth = 1
const deg2rad = 0.01745329
dim x1 As Double = g.Width/2
dim y1 As Double = g.Height/2
dim z As Double = min(x1,y1).75
dim x2,y2 as Double
'Draw 12 arrows radiating out from centre of canvas
for i as Double = 0 to 359 step 30
x2=x1+z
cos(ideg2rad)
y2=y1-z
sin(i*deg2rad)
g.DrawArrow(x1,y1,x2,y2)
next
End Sub
[/code]

The length of arrowhead and the spread angle of the arrowhead are set as properties, so that you can call the DrawArrow routine with the same set of parameters that you would use for DrawLine.

The routine that I posted above doesn’t account for pen size. For a one or two point pen size it won’t be noticeable, but for a large pen size the arrowheads won’t look good. Rather than leave things half done, I revised the routine to compensate for pen size, and while I was at it, I decided to include several arrowhead styles. A new property mArrowType has a range of 0…3 and the corresponding styles are:
0 - Open style (like the one given by Eugene Dakin)

1 - Closed triangle

2 - Filled triangle

3 - Filled polygon (sharp point arrowhead)

Revised code:

Public Sub DrawArrow(extends g As Graphics, x1 As Double, y1 As Double, x2 As Double, y2 As Double)
  'Draw a line with arrowhead at the end
  'Calling parameters are the same as for DrawLine
  'Arrowhead parameters are determined by properties arrowLength and arrowAngle
  '
  '*** Revised to compensate for large pen sizes
  '
  const deg2halfrad = 0.008726646259972
  'Get angle of line
  dim aLine As Double = ATan2(y2-y1,x2-x1)
  'calculate angle of arrowhead lines
  dim ahLineA As Double = aLine-mArrowAngle*deg2halfrad
  dim ahLineB As Double = aLine+arrowAngle*deg2halfrad
  'calculate x and y coordinates of the arrowhead points
  dim ahLineAx As Double = x2-mArrowLength*cos(ahLineA)
  dim ahLineBx As Double = x2-mArrowLength*cos(ahLineB)
  dim ahLineAy As Double = y2-mArrowLength*sin(ahLineA)
  dim ahLineBy As Double = y2-mArrowLength*sin(ahLineB)
  'Pen size adjustment
  dim dPH as Double = g.PenHeight/2
  dim dPW as Double = g.PenWidth/2
  dim poly() As Double =Array(0,x2+dPW,y2+dPH,ahLineAx+dPW,ahLineAy+dPH,ahLineBx+dPW,ahLineBy+dPH)
  
  if mArrowType=0 then
    'Draw the principal line
    g.DrawLine(x1,y1,x2,y2)
  Else
    'Draw the principal line (only to the base of the arrowhead)
    g.DrawLine(x1,y1,(ahLineAx+ahLineBx)/2,(ahLineAy+ahLineBy)/2)
  end if
  if mArrowType<3 then
    'Draw two line segments for arrowhead
    g.DrawLine(ahLineAx,ahLineAy,x2,y2)
    g.DrawLine(ahLineBx,ahLineBy,x2,y2)
  end if
  if mArrowType=1 or mArrowType=2 then
    'Add baseline for triangle arrowhead
    g.DrawLine(ahLineAx,ahLineAy,ahLineBx,ahLineBy)
  end if
  if mArrowType=2 or mArrowType=3 then
    'Fill the arrowhead
    g.FillPolygon(poly)
  end if
End Sub

Project File

Beautiful! You have great taste and way more energy than me…:slight_smile:

These examples are very impressive. Thank you for sharing @Eugene Dakin & @Robert Weaver

I totally agree with Scott. Thank you for sharing @Eugene Dakin & @Robert Weaver

Some time ago I had the same problem in 3D using OpenGL. The position of the plane of the arrowhead was complicate when rotating the model and I decided to use a small cone for it. It has worked well until now:

1 Like

[quote=454708:@Ramon SASTRE]I totally agree with Scott. Thank you for sharing @Eugene Dakin & @Robert Weaver

Some time ago I had the same problem in 3D using OpenGL. The position of the plane of the arrowhead was complicate when rotating the model and I decided to use a small cone for it. It has worked well until now:[/quote]

Share …? :wink:

This is as it is. Probably it can be written better, but I already said it is an old function.

[code]Sub DibuixaUnaFletxa(xi as Double, yi as Double, zi as Double, xf as Double, yf as Double, zf as Double, f As double = 20)

wTess.pbGraf.MakeCurrent

'Draw an arrow from xi,yi,zi to xf,yf,zf
'f = about length of the arrowhead

dim dx as Double = xi - xf
dim dy as Double = yi - yf
dim dz as Double = zi - zf
dim dt as Double = Dist3d(xi, yi, zi, xf, yf, zf)
dim dv as Double = dt / f

'Lnia principal
OpenGL.glBegin(OpenGL.GL_LINES)
OpenGL.glVertex3f(xi, yi, zi)
OpenGL.glVertex3f(xf, yf, zf)
OpenGL.glEnd

'Punta de la fletxa
OpenGL.glMatrixMode(OpenGL.GL_MODELVIEW)
OpenGL.glPushMatrix
OpenGL.glTranslatef (xf, yf, zf)

Dim dxy, Fy, Fz As Double
dxy = Sqrt(dx ^ 2 + dy ^ 2)
Fz = Graus(ATan2(dy, dx))
OpenGL.glRotatef(Fz, 0, 0, 1)
Fy = Graus(ATan2(dxy, dz))
OpenGL.glRotatef(Fy, 0, 1, 0)

Dim Quad As Integer = gluNewQuadric
gluCylinder(quad, 0, dv, dv * 3, 10, 10)
gluDeleteQuadric quad

OpenGL.glPopMatrix

End Sub[/code]