# Thread: Gallery - Drawing tangents to circles

1. ## Gallery - Drawing tangents to circles

Recently I decided to write an algorithm that draws the path of a chain (like a bike chain) as it runs around a set of cogs. Cogs can have any position and radius. However, they must be listed in clockwise order. I've just got it working and i want to post some pictures for your delectation.

I'll post the code at some point (VB.Net).

8 cogs, 3 of them anticlockwise rotation (detected automatically by the alg)...

2. ## Re: Gallery - Drawing tangents to circles

Similar but with a nice blue fill.

3. ## Re: Gallery - Drawing tangents to circles

Define a new region for a form, passes the fanbelt.path object to form.region.

I moved the first cog up a bit so you can see the titlebar of the form, and move it around.

4. ## Re: Gallery - Drawing tangents to circles

OK, here's the first practical demo of how it could be useful...

Race track designer.

(There's a weird artifact in some of the tighter corners, I'll have to find a way to cure that).

5. ## Improved Race Track!

OK, solved the artefacts by using 2 paths instead of one.

Note to hyper geeks: look closely at the bend in the centre of the image. you'll see that one section of white kerb is longer than the others. That is where the 2 ends of the curve path meet each other and overlap. I'm working on a way to eliminate this buts only a geek would notice it

This looks much better...

6. ## Re: Gallery - Drawing tangents to circles

Very nice stuff!!

Is the width of the track done using 2 paths ment to vary?

7. ## Re: Gallery - Drawing tangents to circles

Originally Posted by SLH
Very nice stuff!!

Is the width of the track done using 2 paths ment to vary?
I used different radii for the two paths. Insolent oik!

Real racetracks are not a constant width all the way around.

8. ## Re: Gallery - Drawing tangents to circles

Man, I wish I still had the code for this. Anyone know where I put it?

Seriously.

9. ## Re: Gallery - Drawing tangents to circles

No idea, but this is pretty bad bass. I'd like to work up an algorithm and post it with you.

10. ## Re: Gallery - Drawing tangents to circles

ill use this as a pattern to follow

11. ## Re: Gallery - Drawing tangents to circles

Hi Woss, I suspect that Dave is preoccupied with his latest bundle of joy so maybe I can step in with my own "algorithm". I'll assume you want to create a track from any arrangement of corners, not just those you have illustrated; and you work around the track clockwise.

1. Let's suppose each corner of the track is represented by a point P, an inner radius r and an outer radius R.
2. The points P can be connected up as a polygon which has convex and concave vertices. Get the convexity of each vertex (see code below).
3. Start building the outer graphicspath with the outer radius R1 of the first vertex. Then for each vertex Pn:

4. If the next vertex Pn+1 has the same convexity as Pn you need to find the outer tangent (one which does not cross the line between the centres) for the same radius (inner or outer). So if you are creating the outer path for Pn (Rn), you need the tangent to the outer radius of Pn+1 (Pn+1R). To find the external tangent from PnR to Pn+1R:
a. Get the slope mn between the centres, i.e. Pn-Pn+1, using Math.Atan2.
b. Get the distance Dn between the centres Pn - Pn+1 (by Pythagorus).
c. Find the difference between the radii e.g. diffRn = Pn+1R - PnR. The result can be positive or negative.
d. Get the relative tangent slope, which (I think) will be mRn = Math.ASin(Dn/diffRn). This can also be positive or negative.
e. Subtract the relative slope from the slope between centres: mTn =mn - mRn. This is the actual slope of the tangent. Convert it from radians to degrees and save it.

5. If the next vertex Pn+1 has opposite convexity to Pn, then you need to find the cross tangent (which passes through the line between the circle centres) to the opposite radius (outer or inner). For example, if you are creating the outer path from PnR and Pn is convex, you need the cross-tangent to Pn+1r.
a, b. Get the slope between centres mn and the distance Dn as in 4.
c. Find the sum of the radii sumn = Rn + rn+1 or rn + Rn+1 depending on whether you are going from convex to concave or vice-versa. Make it positive if you are going from convex to concave, and negative from concave to convex.
d. Get the relative tangent slope of the cross tangent nn = Math.Asin(Dn/sumn). This will be correspondingly positive or negative.
e. Add the relative slope to (5a) to get the tangent slope: mTn = mn + nn.

6. If you've survived this far, you have list of tangent angles for each vertex of the outer path. Now you need to find the arc around each vertex and add it to the path.
7. To get the start angle for the arc for vertex Pn, get the tangent slope mTn-1 for the previous vertex. Subtract 90 degrees to get the normal to the tangent.
8. To get the sweep angle for the arc of vertex Pn, get the tangent slope mTn and subtract 90 degrees. Then subtract the start angle. If Pn is concave, make the result negative.
9. Add the arc to the path. There is no need to add the tangents themselves to the path. Just add the arcs and use GraphicsPath.CloseFigure.

10. To get the inner path, repeat all of the above starting from the inner radius of P1. Maybe a few things will have to be the other way round. I'm exhausted already.

Oh yes, here's my simple code for getting the convexity of a polygon vertex. It just removes the vertex from the polygon, then checks if the vertex is inside or outside the resulting polygon. It works reliably for non self-intersecting polygons.
Code:
```	Private Function VertexIsConcave(polygon As PointF(), vertex As PointF) As Boolean
Dim vertices As New List(Of PointF)(polygon)
vertices.Remove(vertex)
Dim gp As New Drawing2D.GraphicsPath
Return gp.IsVisible(vertex)
End Function```
BB

12. ## Re: Gallery - Drawing tangents to circles

Yes, I think you've formally stated in mathematical terms all the stuff that was floating around in my brain while I was writing my old code.

I remember it well. To be honest, I have no idea what is the current state of the art as far as non-accelerated graphics in .Net is. Last time I did this I was hacking around with Brush, Pen, GraphicsPaths and Lockbits and huge amounts of "unmanaged" pointer math code in order to get the framerate up. I have not programmed C# seriously in any IDE more recent than VS2005 so I can only be of limited assistance.

Looks like you've got a good handle on it though. I eagerly await your first screenshots

13. ## Re: Gallery - Drawing tangents to circles

I'm glad I didn't tackle this, I'm terrible in math.

14. ## Re: Gallery - Drawing tangents to circles

Heh, me too. Luckily there's not too much math involved, the trigonometry is little more than atan2() and a bit of logic to decide if 3 consecutive points go clockwise or anticlockwise. The hardest part was actually fixing the weird .Net graphical issues.

You should give it a shot too, it's good fun to mess with C# graphics in a simple 2D context like this. I learned a lot from this old project.

15. ## Re: Gallery - Drawing tangents to circles

Hmm, I wonder if my old code is on that old maxtor caddy at the back of my shelf...

16. ## Re: Gallery - Drawing tangents to circles

Originally Posted by wossname
Yes, I think you've formally stated in mathematical terms all the stuff that was floating around in my brain while I was writing my old code.

I remember it well. To be honest, I have no idea what is the current state of the art as far as non-accelerated graphics in .Net is. Last time I did this I was hacking around with Brush, Pen, GraphicsPaths and Lockbits and huge amounts of "unmanaged" pointer math code in order to get the framerate up. I have not programmed C# seriously in any IDE more recent than VS2005 so I can only be of limited assistance.

Looks like you've got a good handle on it though. I eagerly await your first screenshots
I'll see if I can work something out in the weekend (see also my PM). I'm sure GDI+ will be fast enough for designing the track. Getting the cars to race around it convincingly may be another story.

Edit: @dave. Once the track is designed, I assume you could port the design as a texture, together with the path boundaries as polygons, to XNA. I would happily leave that to an XNAxpert.

BB

17. ## Re: Gallery - Drawing tangents to circles

Sounds simple enough, I actually have a method that converts bitmaps to Texture2ds which is what this would entail. If I have the track, I could certainly build something that would make cars move around that track.

18. ## Re: Gallery - Drawing tangents to circles

Let those who thought I couldn't do it weep. Here's a pic of how the race track looks in Edit mode. The light gray dots are the corner centres, which can be dragged to any position. The yellow dot is the selected dot. The dashed circles around it are the corner's inner and outer radii, which you can adjust with the mouse wheel. These details are hidden in Play mode. The dashed kerb was no problem whatsoever (Pen.DashPattern) but the logic of the tangent angles and arcs kept me up all night. There's a few minor bugs (aren't there always) still to sort out and a few features still to implement (adding/deleting corners in Edit mode).

19. ## Re: Gallery - Drawing tangents to circles

Alright boops boops, I've tried my hand at following your math, but I must say that I'm just ignorant when it comes to math... Here are my classes:

Code:
```Option Strict On
Option Explicit On
Public Class Cog

#Region "Properties"

Private pt As PointF
<System.ComponentModel.Description("Gets/Sets the center point of the cog.")> _
Public Property Location() As PointF
Get
Return pt
End Get
Set(ByVal value As PointF)
pt = value
End Set
End Property

Private iRadius As Single
<System.ComponentModel.Description("Gets/Sets the distance in pixels from the Location to the inner radius of the cog.")> _
Public Property InnerRadius() As Single
Get
End Get
Set(ByVal value As Single)
End Set
End Property

Private oRadius As Single
<System.ComponentModel.Description("Gets/Sets the distance in pixels from the Location to the outer radius of the cog.")> _
Public Property OuterRadius() As Single
Get
End Get
Set(ByVal value As Single)
End Set
End Property

#End Region

#Region "New Constructors"

Sub New()
pt = New Point(0, 0)
End Sub

Sub New(ByVal inner As Single, ByVal outer As Single)
End Sub

#End Region

End Class```
And...

Code:
```Option Strict On
Option Explicit On
Public Class Track
Inherits System.Windows.Forms.Control

#Region "Properties"

Private cogCollection As Cog()
<System.ComponentModel.Description("Gets the collection of cogs that make up the track. NOTE: The order of the cogs plays a huge importance in the layout of the track.")> _
Public ReadOnly Property Cogs() As Cog()
Get
Return cogCollection
End Get
End Property

Private isDash As Boolean
<System.ComponentModel.Description("Gets/Sets if the outside border of the track is dashed.")> _
Public Property DashTrack() As Boolean
Get
Return isDash
End Get
Set(ByVal value As Boolean)
isDash = value
End Set
End Property

Private dashPrim As Color
<System.ComponentModel.Description("Gets/Sets the primary dash color of the track if the DashTrack property is True.")> _
Public Property PrimaryDashColor() As Color
Get
Return dashPrim
End Get
Set(ByVal value As Color)
dashPrim = value
End Set
End Property

Private dashSecond As Color
<System.ComponentModel.Description("Gets/Sets the secondary dash color of the track if the DashTrack property is True.")> _
Public Property SecondaryDashColor() As Color
Get
Return dashSecond
End Get
Set(ByVal value As Color)
dashSecond = value
End Set
End Property

#End Region

#Region "Methods"

'Public Methods
Public Sub Add(ByVal cog As Cog)
'Convert the cogCollection to a list and add the cog to that list
'Convert the list back to an array
Dim l As List(Of Cog) = cogCollection.ToList
cogCollection = l.ToArray
End Sub

Public Sub AddRange(ByVal cogs() As Cog)
'Convert the cogCollection to a list and add the cogs to that list
'Convert the list back to an array
Dim l As List(Of Cog) = cogCollection.ToList
cogCollection = l.ToArray
End Sub

Public Sub ChangeOrder(ByVal priorIndex As Integer, ByVal newIndex As Integer)
'Convert the cogCollection to a list and get the cog at the prior index
'Insert the cog at the new index
'Convert the list back to an array
Dim l As List(Of Cog) = cogCollection.ToList
Dim c As Cog = l.Item(priorIndex)
l.Remove(c)
l.Insert(newIndex, c)
cogCollection = l.ToArray
End Sub

Public Sub Remove(ByVal cog As Cog)
'Convert the cogCollection to a list and remove the cog from that list
'Convert the list back to an array
Dim l As List(Of Cog) = cogCollection.ToList
l.Remove(cog)
cogCollection = l.ToArray
End Sub

Public Sub RemoveAt(ByVal index As Integer)
'Convert the cogCollection to a list and remove the cog by it's index
'Convert the list back to an array
Dim l As List(Of Cog) = cogCollection.ToList
l.RemoveAt(index)
cogCollection = l.ToArray
End Sub

'Private Methods
Private Function CreatePath() As Drawing2D.GraphicsPath
Dim path As List(Of PointF) = New List(Of PointF)
Dim oRadius As List(Of Drawing2D.GraphicsPath) = New List(Of Drawing2D.GraphicsPath)
Dim iRadius As List(Of Drawing2D.GraphicsPath) = New List(Of Drawing2D.GraphicsPath)

For Each c As Cog In cogCollection
'Get the inner and outer radius rectangle of the cog
Dim iRect As RectangleF = New RectangleF(New PointF(c.Location.X - c.InnerRadius, c.Location.Y - c.InnerRadius), New SizeF(c.InnerRadius * 2, c.InnerRadius * 2))
Dim oRect As RectangleF = New RectangleF(New PointF(c.Location.X - c.OuterRadius, c.Location.Y - c.OuterRadius), New SizeF(c.OuterRadius * 2, c.OuterRadius * 2))

'Add the inner radius of the cog to a graphics path for the inner rectangle
Dim iGraphics As Drawing2D.GraphicsPath = New Drawing2D.GraphicsPath

'Add the outer radius of the cog to a graphics path for the inner rectangle
Dim oGraphics As Drawing2D.GraphicsPath = New Drawing2D.GraphicsPath

'Add the inner and outer radius graphics path to their list
Next

'Add the outer radius of the first vertex

'Loop through each vertex
For i As Integer = 0 To oRadius.Item(0).PathPoints.Count - 1

Next

Return Nothing
End Function

'Thank you boops boops for this function
Private Function VertexIsConcave(ByVal polygon As PointF(), ByVal vertex As PointF) As Boolean
Dim vertices As List(Of PointF) = New List(Of PointF)(polygon)
vertices.Remove(vertex)
Dim gp As New Drawing2D.GraphicsPath
Return gp.IsVisible(vertex)
End Function

#End Region

#Region "Events"

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)

End Sub

#End Region

End Class```
I cannot get past step 3!!!

20. ## Re: Gallery - Drawing tangents to circles

G'day dday,

Your step 3 is only part of the story as far as I am concerned. I frittered away many a frustrated hour trying to develop my initial attempt into a bug-free track designer with "a few extra features". Well, that was in January and February: by mid March I had an imaginary sign flashing in front of my screen every few minutes with the message "get a life!" Still, most of it is now working as intended. I'll send you a PM with a link to my code.

Here's a new pic illustrating features like overlapping and multiple paths:

A comment about your code. Wouldn't it be easier to declare CogCollection as a List(Of Cog)? Then you wouldn't need those conversion methods.

BB

21. ## Re: Gallery - Drawing tangents to circles

A comment about your code. Wouldn't it be easier to declare CogCollection as a List(Of Cog)? Then you wouldn't need those conversion methods.
That is how I initially had it, and to be honest I don't know why I changed it.

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•

Featured

Click Here to Expand Forum to Full Width