﻿ VB Helper: HowTo: Draw an Apollonian gasket (or Apollonian packing) in Visual Basic .NET

 Home Search   What's New Index Books Links Q & A Newsletter Banners   Feedback Tip Jar MSDN Visual Basic Community

Title Draw an Apollonian gasket (or Apollonian packing) in Visual Basic .NET This example shows how to draw an Apollonian gasket (or Apollonian packing) in Visual Basic .NET. mathematics, algorithms, graphics, Apollonian gasket, Apollonian packing, Apollonius' Problem, Apollonius, Apollonian circles, tangent cicles, geometry, example, example program, Windows Forms programming, Visual Basic .NET, VB.NET Algorithms, Graphics

The example Find circles that are tangent to three given circles in Visual Basic .NET shows how to find up to eight circles that are tangent to three given circles. This example uses that method to build an Apollonian gasket.

(This is also called an Apollonian packing. If you think of each circle as cutting a hole in the enclosing circle so you get a lace-like figure, it's a gasket. If you think of filling the enclosing circle with other circles, it's a packing.)

The following FindApollonianPacking method controls the drawing and returns a Bitmap holding the packing.

' Find the Apollonian packing.
Private Function FindApollonianPacking(ByVal width As _
Integer) As Bitmap
Dim bm As New Bitmap(width, width)
Using gr As Graphics = Graphics.FromImage(bm)
gr.SmoothingMode = SmoothingMode.AntiAlias
gr.Clear(Color.LightGreen)

' Create the three central tangent circles.
Dim radius As Single = width * 0.225F
Dim x As Single = width \ 2
Dim gasket_height As Single = 2 * CSng(radius + 2 * _
Dim y As Single = (width - gasket_height) / 2 + _
Dim circle0 As New Circle(x, y, radius)

' Draw a box around the gasket. (For debugging.)
'gr.DrawRectangle(Pens.Orange, _
'    x - gasket_height / 2, _

Dim circle1 As New Circle(x, y, radius)
Dim circle2 As New Circle(x, y, radius)

' Draw the three central circles.
circle0.Draw(gr, Pens.Blue)
circle1.Draw(gr, Pens.Blue)
circle2.Draw(gr, Pens.Blue)

' Find the circle that contains them all.
Dim big_circle As Circle = FindApollonianCircle( _
circle0, circle1, circle2, -1, -1, -1)
big_circle.Draw(gr, Pens.Blue)

' Set level to smaller values such as 3 to see
Dim level As Integer = 10000

' Find the central circle.
FindCircleOutsideAll(level, gr, circle0, circle1, _
circle2)

' Find circles tangent to the big circle.
FindCircleOutsideTwo(level, gr, circle0, circle1, _
big_circle)
FindCircleOutsideTwo(level, gr, circle1, circle2, _
big_circle)
FindCircleOutsideTwo(level, gr, circle2, circle0, _
big_circle)
End Using
Return bm
End Function

This method starts by creating a bitmap of the desired size. It then creates the three largest circles inside the enclosing circle. It does a little math to size the circles appropriately, arranges them so they are tangent, and then draws them.

Next the code calls the FindApollonianCircle method used by the previous example to find a circle tangent to the three large circles. It passes the parameters -1, -1, and -1 to find the tangent circle that contains all three of the big circles so the result is the enclosing circle. The program draws the enclosing circle.

The code then calls the FindCircleOutsideAll method to find and draw the small circle that sits between the three initial circles. It finishes by calling FindCircleOutsideTwo three times to find the circles that lie inside the enclosing circle but outside of two of the inner big circles.

The following code shows the FindCircleOutsideAll method.

' Draw a circle tangent to these three circles and that is
' outside all three.
Private Sub FindCircleOutsideAll(ByVal level As Integer, _
ByVal gr As Graphics, ByVal circle0 As Circle, ByVal _
circle1 As Circle, ByVal circle2 As Circle)
Dim new_circle As Circle = FindApollonianCircle(circle0, _
circle1, circle2, 1, 1, 1)
If (new_circle Is Nothing) Then Return
If (new_circle.Radius < 0.1) Then Return

new_circle.Draw(gr, Pens.Blue)

level -= 1
If (level > 0) Then
FindCircleOutsideAll(level, gr, circle0, circle1, _
new_circle)
FindCircleOutsideAll(level, gr, circle0, circle2, _
new_circle)
FindCircleOutsideAll(level, gr, circle1, circle2, _
new_circle)
End If
End Sub

The FindCircleOutsideAll method finds a circle tangent to three other circles that does not contain any of the three. In this program that is the circle that lies between the three other circles. For example, the very center circle lies between the three large circles.

The code calls the FindApollonianCircle method used by the previous example program, passing it the parameters 1, 1, 1 to find the desired circle. If it finds such a circle, the code draws it.

The code then decrements the level parameter and if it is still greater than 0 the method calls itself recursively three times to find the circles between the new circle and the pairs of the original three circles. (Study the picture to see where those areas are.)

The following code shows the FindCircleOutsideTwo method.

' Draw a circle tangent to these three circles and that is
' outside two of them.
Private Sub FindCircleOutsideTwo(ByVal level As Integer, _
ByVal gr As Graphics, ByVal circle0 As Circle, ByVal _
circle1 As Circle, ByVal circle_contains As Circle)
Dim new_circle As Circle = FindApollonianCircle( _
circle0, circle1, circle_contains, 1, 1, -1)
If (new_circle Is Nothing) Then Return
If (new_circle.Radius < 0.1) Then Return

new_circle.Draw(gr, Pens.Blue)

level -= 1
If (level > 0) Then
FindCircleOutsideTwo(level, gr, new_circle, circle0, _
circle_contains)
FindCircleOutsideTwo(level, gr, new_circle, circle1, _
circle_contains)
FindCircleOutsideAll(level, gr, circle0, circle1, _
new_circle)
End If
End Sub

This method finds a circle that is inside one circle and outside two others. For example, in the figure this method draws the medium-sized circle on the upper right that lies inside the enclosing circle and outside the two big circles.

The code calls the FindApollonianCircle method passing it the parameters 1, 1, and -1 to find the appropriate circle. Note that the order in which the code passes the circles to that method is important because the last circle is the one that should contain the new circle.

Next the code decrements the level parameter and if it is still greater than 0 the method calls itself recursively three times to find the circles in the areas it has just created. (Again study the picture to see where those areas are.)