Prime spiral with a twist#

What is a prime spiral?#


If you create a square spiral of numbers from the center and highlight each number that is prime, you create a Ulam spiral.

What is fascinating about the image that is produced is that there are distinct diagonal lines where primes are much more likely to be. Clearly, there is a pattern to primes. They are not randomly distributed. This is the result.

Program output with a circle radius of 1 pixel
Program output with a circle radius of 1 pixel

Numberphile has an interesting video about Ulam spirals

The Code#


The first class is just an ellipse organizational structure, and the second is an abstract idea that represents a move. Since the lines of numbers increment length every two 90 degree turns, keeping two turns in one move made the structure easier to iterate. I create all the moves first and place them in a list before checking the primality of any of the numbers or drawing anything on the bitmap.

public class Ellipse
{
    public int X { get; set; }
    public int Y { get; set; }
    public int W { get; set; }
    public int H { get; set; }
    public Ellipse(int x, int y, int w, int h)
    {
        X = x;
        Y = y;
        W = w;
        H = h;
    }
}

public class Move
{
    public int Length { get; set; }
    public string Direction1 { get; set; }
    public string Direction2 { get; set; }

    public Move(int length, string direction1, string direction2)
    {
        Length = length;
        Direction1 = direction1;
        Direction2 = direction2;
    }
}

Using the above structure I create a list of moves with the following function:

private void makeMoveList()
{
    string direction = "R";
    for (int length = 1; length < (int)(0.98 * this.ClientRectangle.Height/radius); length++)
    {
        if (direction == "R")
        {
            moveList.Add(new Move(length, "R", "U"));
            direction = "L";
        }
        else if (direction == "U")
        {
            moveList.Add(new Move(length, "U", "L"));
            direction = "D";
        }
        else if (direction == "L")
        {
            moveList.Add(new Move(length, "L", "D"));
            direction = "R";
        }
        else if (direction == "D")
        {
            moveList.Add(new Move(length, "D", "R"));
            direction = "U";
        }
    }    
}

Next, I plot the points of the spiral by iterating the move list and checking all the numbers in each move to see if they are prime. The primality determines if the circle at a given point is blue or invisible. If the number is prime, I add a new circle to the circle list. If not, I just move along to the next position in the spiral without recording the position in the circle list.

private void plotSpiral()
{
     int pixelNumber = 0;
    Point pos = new Point(b.Width / 2, b.Height / 2);

    foreach (Move m in moveList)
    {
        //Direction 1
        for (int i = 0; i < m.Length; i++)
        {
            if (getPixelColor(pixelNumber) == Color.Blue)
            {
                if (m.Direction1 == "R") circleList.Add(new Ellipse(pos.X += radius, pos.Y, diam, diam));
                else if (m.Direction1 == "U") circleList.Add(new Ellipse(pos.X, pos.Y -= radius, diam, diam));
                else if (m.Direction1 == "L") circleList.Add(new Ellipse(pos.X -= radius, pos.Y, diam, diam));
                else if (m.Direction1 == "D") circleList.Add(new Ellipse(pos.X, pos.Y += radius, diam, diam));

            }
            else
            {
                if (m.Direction1 == "R") pos.X += radius;
                else if (m.Direction1 == "U") pos.Y -= radius;
                else if (m.Direction1 == "L") pos.X -= radius;
                else if (m.Direction1 == "D") pos.Y += radius;
            }
        pixelNumber++;
    }

        //Direction 2
        for (int i = 0; i < m.Length; i++)
        {
            if (getPixelColor(pixelNumber) == Color.Blue)
            {
                if (m.Direction2 == "R") circleList.Add(new Ellipse(pos.X += radius, pos.Y, diam, diam));
                else if (m.Direction2 == "U") circleList.Add(new Ellipse(pos.X, pos.Y -= radius, diam, diam));
                else if (m.Direction2 == "L") circleList.Add(new Ellipse(pos.X -= radius, pos.Y, diam, diam));
                else if (m.Direction2 == "D") circleList.Add(new Ellipse(pos.X, pos.Y += radius, diam, diam));
            }
            else
            {
                if (m.Direction2 == "R") pos.X += radius;
                else if (m.Direction2 == "U") pos.Y -= radius;
                else if (m.Direction2 == "L") pos.X -= radius;
                else if (m.Direction2 == "D") pos.Y += radius;
            }
        pixelNumber++;
        }
    }                        
}

private Color getPixelColor(int pixelNumber)
{
    if (IsPrime(pixelNumber)) return Color.Blue;
    else return Color.White;
}

public static bool IsPrime(int number)
{
    if (number <= 1) return false;
    if (number == 2) return true;
    if (number % 2 == 0) return false;

    var boundary = (int)Math.Floor(Math.Sqrt(number));

    for (int i = 3; i <= boundary; i += 2)
        if (number % i == 0)
            return false;

    return true;
}

The final part of the program is straightforward. I set the bitmap as a graphics object and iterate through the circle list, plotting ellipses. If the circle intersects with any other circle on the list it will be blue. If not, it will be red.

    private void showSpiral()
    {
        using (Graphics g = Graphics.FromImage(b))
        {
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.Clear(Color.White);
            foreach (Ellipse circle in circleList)
            {
                if (checkIntersect(circle) == true)                    
                    g.DrawEllipse(Pens.Blue, new Rectangle(circle.X, circle.Y, circle.W, circle.H));                    
                else
                    g.DrawEllipse(Pens.Red, new Rectangle(circle.X, circle.Y, circle.W, circle.H));                                            
            }
        }
        pictureBox1.Image = b;
    }

    private bool checkIntersect(Ellipse testCircle)
    {
        int count = 0;
        bool intersect = false;
        foreach(Ellipse circle in circleList)
        {
            PointF intersect1, intersect2;
            FindCircleCircleIntersections(testCircle.X, testCircle.Y, testCircle.W/2, circle.X, circle.Y, circle.W/2, out intersect1, out intersect2);
            if((!float.IsNaN(intersect1.X) && !float.IsNaN(intersect1.Y)) || (!float.IsNaN(intersect2.X) && !float.IsNaN(intersect2.Y)))
            {
                intersect = true;
                count++;
            }
        }
        return intersect;
    }
Program output with a circle radius of 12 pixels
Program output with a circle radius of 12 pixels
Program output with a circle radius of 5 pixels
Program output with a circle radius of 5 pixels