First 100 Numbers Poster C# Generator

This simple C# program generates some nice posters containing numbers, e.g. from 1 to 100, highlighting specific numbers like powers of 2, primes and fibonacci numbers. Inspiration for this mini-project were two little kids and a wooden hundred-board with numbers, then the idea was extrapolated and this is how such generated PNGs look like:

Larger PNG images for download:   100-deca-only ,   256-hexa-only ,   100-deca-special ,   256-hexa-special

You can download the project (Visual Studio 2017 Windows Forms application) ZIP file here: hundredBoard.zip

If you like the generator, you can adapt it and print some nice large posters on a fancy color printer. This is how you can customize some input parameters:

See the code below for more details (you can create new project in VS and paste this code to replace Form.cs):

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Linq;
using System.Windows.Forms;

namespace hundredBoard
{
    public partial class NumbersForm : Form
    {
        public bool GenerateHexa = false; //whether generate 100 board e.g. from 1 to 100, or generate 256 hexa board e.g. from 0 do FF (255)
        public bool DrawSpecialNumbers = true; //whether display primes, fibonaccis and powers of 2

        public int SettingResizeFactor = 100; //for large bitmaps (e.g. for printing), use 200
        public int SettingStroke = 4; //percentage how thick the stroke effect is

        public int TotalLines;
        public int NumbersPerLine;
        public int NumberShift;

        public int SettingTextSizeFactor;

        public Bitmap GeneratedBitmap;

        public readonly SortedSet FibonacciListInt32 = new SortedSet
        {
            0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229,
            832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733,
            1134903170, 1836311903 // 2971215073 is already too big for int32 (and is a prime number)
        };

        public NumbersForm()
        {
            InitializeComponent();

            //set these values to display desired numbers
            TotalLines = 10;
            NumbersPerLine = 10;
            NumberShift = 1; //we start from 1 (don't go over 1000)
            if (GenerateHexa)
            {
                TotalLines = 16;
                NumbersPerLine = 16;
                NumberShift = 0; //when generating hexa, let's start from 0 (and the last number will be e.g. FF)
            }

            //set dimension of the generated image
            generatedPictureBox.Width = 1 + (NumbersPerLine * 200 * SettingResizeFactor) / 100;
            generatedPictureBox.Height = 1 + (TotalLines * 200 * SettingResizeFactor) / 100;

            GeneratedBitmap = new Bitmap(generatedPictureBox.Width, generatedPictureBox.Height);
            var graphics = Graphics.FromImage(GeneratedBitmap);
            graphics.SmoothingMode = SmoothingMode.AntiAlias;

            DrawStuff(graphics); //generate image

            GeneratedBitmap.Save(GenerateHexa ? "hexaBoard.png" : "decaBoard.png", ImageFormat.Png); //save image to file

            panel1.AutoScroll = true; //display image
            generatedPictureBox.SizeMode = PictureBoxSizeMode.AutoSize;
            generatedPictureBox.Image = GeneratedBitmap;
        }

        //Draws numbers table (core method)
        public void DrawStuff(Graphics g)
        {
            g.TextRenderingHint = TextRenderingHint.AntiAlias;

            var sf = new StringFormat
            {
                LineAlignment = StringAlignment.Center,
                Alignment = StringAlignment.Center
            };

            //calculate some settings for positioning of elements
            SettingTextSizeFactor = 90;
            var fontNormal = new Font("calibri", (float)SettingTextSizeFactor * SettingResizeFactor / 100, FontStyle.Bold);

            if (GenerateHexa)
            {
                fontNormal = new Font("calibri", (float)SettingTextSizeFactor * SettingResizeFactor / 120, FontStyle.Bold);
            }

            var font100 = new Font("calibri", (float)SettingTextSizeFactor * SettingResizeFactor / 150, FontStyle.Bold);
            var fontMini = new Font("calibri", (float)SettingTextSizeFactor * SettingResizeFactor / 200, FontStyle.Bold);

            //drawing squares
            for (var i = 0; i < NumbersPerLine; i++)
            {
                for (var j = 0; j < TotalLines; j++)
                {
                    int number = i + j * NumbersPerLine + NumberShift; //which number is being drawn

                    string decnumber = number.ToString();
                    string hexnumber = number.ToString("X").ToUpper();
                    string displaynumber = decnumber;

                    if (GenerateHexa) displaynumber = hexnumber;

                    int x = (200 * i * SettingResizeFactor) / 100; //x position
                    int y = (200 * j * SettingResizeFactor) / 100; //y position
                    int stroke0 = (SettingStroke * SettingResizeFactor) / 100; //outside stroke effect

                    Font font = fontNormal;
                    if (number >= NumbersPerLine * NumbersPerLine)
                    {
                        font = font100;
                    }

                    //render square inside
                    Brush brush = Brushes.CornflowerBlue;
                    if (GenerateHexa)
                    {
                        brush = Brushes.LimeGreen;
                    }
                    g.FillRectangle(brush, x, y, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100);

                    //if prime
                    if (IsPrime(number))
                    {
                        var brush2 = GenerateHexa ? Brushes.DarkGreen : Brushes.DarkBlue;
                        g.FillRectangle(brush2, x + (10 * SettingResizeFactor) / 100, y + (10 * SettingResizeFactor) / 100, (180 * SettingResizeFactor) / 100, (180 * SettingResizeFactor) / 100);
                    }

                    int size2n = 64;

                    //render 2^n background
                    if (GenerateHexa)
                    {
                        if (IsPowerOfTwo(Convert.ToUInt64(number)))
                        {
                            Brush brush3 = Brushes.Red;
                            Pen pen3 = new Pen(Color.Red, 0);
                            DrawAndFillRoundedRectangle(g, pen3, brush3,
                                new Rectangle(x + ((200 - 10 - size2n) * SettingResizeFactor) / 100, y + (10 * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100),
                                (32 * SettingResizeFactor) / 100, (32 * SettingResizeFactor) / 100, RectAngles.All);
                        }

                        //if fibonacci
                        if (DrawSpecialNumbers)
                        {
                            if (FibonacciListInt32.Contains(number))
                            {
                                Brush brush3 = new SolidBrush(Color.FromArgb(255, 255, 48, 0));
                                Pen pen3 = new Pen(Color.FromArgb(255, 255, 48, 0), 0);
                                DrawAndFillRoundedRectangle(g, pen3, brush3,
                                    new Rectangle(x + ((10) * SettingResizeFactor) / 100, y + (10 * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100),
                                    (32 * SettingResizeFactor) / 100, (32 * SettingResizeFactor) / 100, RectAngles.All);
                            }
                        }
                    }

                    //render square frame
                    Pen pen = new Pen(Color.Black, 1);
                    g.DrawRectangle(pen, x, y, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100);

                    //if 2^n
                    if (IsPowerOfTwo(Convert.ToUInt64(number)))
                    {
                        Brush brush2N = Brushes.White;

                        if (GenerateHexa)
                        {
                            brush2N = Brushes.Yellow;
                        }

                        if (GenerateHexa)
                        {
                            g.DrawString("2", fontMini, brush2N, new Rectangle(x + (133 * SettingResizeFactor) / 100, y + (5 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100));
                        }
                        else
                        {
                            g.DrawString("2", fontMini, brush2N, new Rectangle(x + (133 * SettingResizeFactor) / 100, y + (5 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100));

                            Pen pen2 = new Pen(Color.White, 2 * (float)SettingResizeFactor / 100);
                            DrawRoundedRectangle(g, pen2, x + ((200 - 10 - size2n) * SettingResizeFactor) / 100, y + (10 * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100, (8 * SettingResizeFactor) / 100, (8 * SettingResizeFactor) / 100);
                        }
                    }

                    //if fibonacci (up to int32)
                    if (DrawSpecialNumbers)
                    {
                        if (FibonacciListInt32.Any(item => item == number))
                        {
                            Brush brush2N = Brushes.White;

                            if (GenerateHexa)
                            {
                                brush2N = new SolidBrush(Color.FromArgb(255, 255, 192, 96));
                            }

                            if (GenerateHexa)
                            {
                                g.DrawString("F", fontMini, brush2N,
                                    new Rectangle(x + (17 * SettingResizeFactor) / 100,
                                        y + (5 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100,
                                        (200 * SettingResizeFactor) / 100));
                            }
                            else
                            {
                                g.DrawString("F", fontMini, brush2N,
                                    new Rectangle(x + (17 * SettingResizeFactor) / 100,
                                        y + (5 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100,
                                        (200 * SettingResizeFactor) / 100));

                                Pen pen2 = new Pen(Color.White, 2 * (float)SettingResizeFactor / 100);
                                DrawRoundedRectangle(g, pen2, x + ((10) * SettingResizeFactor) / 100,
                                    y + (10 * SettingResizeFactor) / 100, (size2n * SettingResizeFactor) / 100,
                                    (size2n * SettingResizeFactor) / 100, (8 * SettingResizeFactor) / 100,
                                    (8 * SettingResizeFactor) / 100);
                            }
                        }
                    }

                    //y has to be moved a bit lower
                    y = ((200 * j + 5) * SettingResizeFactor) / 100; //y

                    if (GenerateHexa)
                    {
                        y = ((200 * j) * SettingResizeFactor) / 100;
                    }

                    //render stroke effect0
                    if (stroke0 > 0)
                    {
                        for (var k = 0; k < stroke0; k++)
                        {
                            for (var m = 0; m < stroke0; m++)
                            {
                                if (k * k + m * m <= stroke0 * stroke0)
                                {
                                    g.DrawString(displaynumber, font, Brushes.Black, new Rectangle(x + k, y + m, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);
                                    g.DrawString(displaynumber, font, Brushes.Black, new Rectangle(x + k, y - m, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);
                                    g.DrawString(displaynumber, font, Brushes.Black, new Rectangle(x - k, y + m, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);
                                    g.DrawString(displaynumber, font, Brushes.Black, new Rectangle(x - k, y - m, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);
                                }
                            }
                        }
                    }

                    //render main text
                    g.DrawString(displaynumber, font, Brushes.White, new Rectangle(x, y, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);

                    //render dec text in case of hex
                    if (GenerateHexa)
                    {
                        g.DrawString(decnumber, fontMini, Brushes.White, new Rectangle(x, y + (63 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100, (200 * SettingResizeFactor) / 100), sf);
                    }
                }
            }
        }

        //Calculates whether number is a prime number
        private bool IsPrime(int number)
        {
            if (DrawSpecialNumbers)
            {
                if (number < 2) return false;
                if (number % 2 == 0) return (number == 2);
                var root = (int) Math.Sqrt(number);
                for (var i = 3; i <= root; i += 2)
                {
                    if (number % i == 0) return false;
                }

                return true;
            }

            return false;
        }

        //Calculates whether number is a power of 2 (elegant)
        private bool IsPowerOfTwo(ulong x)
        {
            if (DrawSpecialNumbers)
            {
                return x != 0 && (x & (x - 1)) == 0;
            }

            return false;
        }

        //Draws rounded rectangle
        private void DrawRoundedRectangle(Graphics g, Pen p, int x, int y, int w, int h, int rx, int ry)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddArc(x, y, rx + rx, ry + ry, 180, 90);
            path.AddLine(x + rx, y, x + w - rx, y);
            path.AddArc(x + w - 2 * rx, y, 2 * rx, 2 * ry, 270, 90);
            path.AddLine(x + w, y + ry, x + w, y + h - ry);
            path.AddArc(x + w - 2 * rx, y + h - 2 * ry, rx + rx, ry + ry, 0, 91);
            path.AddLine(x + rx, y + h, x + w - rx, y + h);
            path.AddArc(x, y + h - 2 * ry, 2 * rx, 2 * ry, 90, 91);
            path.CloseFigure();
            g.DrawPath(p, path);
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            panel1.Width = Width - 40;
            panel1.Height = Height - 63;
        }

        [Flags]
        public enum RectAngles
        {
            None = 0,
            TopLeft = 1,
            TopRight = 2,
            BottomLeft = 4,
            BottomRight = 8,
            All = TopLeft | TopRight | BottomLeft | BottomRight
        }

        //Draws and fills rounded rectangle (source: probably stackoverflow :)
        public void DrawAndFillRoundedRectangle(Graphics g, Pen p, Brush b, Rectangle r, int horizontalDiameter, int verticalDiameter, RectAngles rectAngles)
        {
            int x = r.X;
            int y = r.Y;
            int width = r.Width;
            int height = r.Height;

            // adapt horizontal and vertical diameter if the rectangle is too little
            if (width < horizontalDiameter)
                horizontalDiameter = width;
            if (height < verticalDiameter)
                verticalDiameter = height;

            /*
             * the drawing is as follows:
             *
             *             a
             *      P______________Q
             *    h /              \ b
             *   W /                \R
             *    |                  |
             *  g |                  | c
             *   V|                  |S
             *    f\                / d
             *     U\______________/T
             *             e
             */

            bool tl = (rectAngles & RectAngles.TopLeft) != 0,
                tr = (rectAngles & RectAngles.TopRight) != 0,
                br = (rectAngles & RectAngles.BottomRight) != 0,
                bl = (rectAngles & RectAngles.BottomLeft) != 0;
            Point pointP = tl ?
                new Point(x + horizontalDiameter / 2, y) :
                new Point(x, y);
            Point pointQ = tr ?
                new Point(x + width - horizontalDiameter / 2 - 1, y) :
                new Point(x + width - 1, y);
            Point pointR = tr ?
                new Point(x + width - 1, y + verticalDiameter / 2) :
                pointQ;
            Point pointS = br ?
                new Point(x + width - 1, y + height - verticalDiameter / 2 - 1) :
                new Point(x + width - 1, y + height - 1);
            Point pointT = br ?
                new Point(x + width - horizontalDiameter / 2 - 1, y + height - 1) :
                pointS;
            Point pointU = bl ?
                new Point(x + horizontalDiameter / 2, y + height - 1) :
                new Point(x, y + height - 1);
            Point pointV = bl ?
                new Point(x, y + height - verticalDiameter / 2 - 1) :
                pointU;
            Point pointW = tl ?
                new Point(x, y + verticalDiameter / 2) :
                pointP;
            using (GraphicsPath gp = new GraphicsPath())
            {
                // a
                gp.AddLine(pointP, pointQ);
                // b
                if (tr)
                    gp.AddArc(x + width - horizontalDiameter - 1, y, horizontalDiameter, verticalDiameter, 270, 90);
                // c
                gp.AddLine(pointR, pointS);
                // d
                if (br)
                    gp.AddArc(x + width - horizontalDiameter - 1, y + height - verticalDiameter - 1, horizontalDiameter, verticalDiameter, 0, 90);
                // e
                gp.AddLine(pointT, pointU);
                // f
                if (bl)
                    gp.AddArc(x, y + height - verticalDiameter - 1, horizontalDiameter, verticalDiameter, 90, 90);
                // g
                gp.AddLine(pointV, pointW);
                // h
                if (tl)
                    gp.AddArc(x, y, horizontalDiameter, verticalDiameter, 180, 90);
                // end
                gp.CloseFigure();
                // draw
                if (b != null)
                    g.FillPath(b, gp);
                if (p != null)
                    g.DrawPath(p, gp);
            }
        }
    }
}