Click here to Skip to main content
15,892,965 members
Articles / Programming Languages / C#
Tip/Trick

Rendering of Segmented Numbers

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
31 Jul 2015CPOL 12.5K   244   4   6
Explains how numbers can be rendered like segmented numbers (looking like old-school digital clock)

Introduction

Before a while, I needed to implement rendering of numbers in one project (C++ MFC), where it wasn't possible to simply use rendering of text using font. So I made the prototype in C# WinForms and maybe someone will find it useful.

Background

I'm using classical old-school approach -> 7-segments rendering of numbers.

Source Code

I've created SegmentRender class for rendering of segmented numbers.

At first, I've created Enum of segment type for better orientation.

C#
private enum Segment
{
    Up, LeftUp, RightUp, Middle, LeftDown, RightDown, Down
}

Here is simple 2D array [10 digits, 7 segments] for definition of segments used for each digit:

C#
static int[,] segValues =
{
       {1, 1, 1, 0, 1, 1, 1},
       {0, 0, 1, 0, 0, 1, 0},
       {1, 0, 1, 1, 1, 0, 1},
       {1, 0, 1, 1, 0, 1, 1},
       {0, 1, 1, 1, 0, 1, 0},
       {1, 1, 0, 1, 0, 1, 1},
       {1, 1, 0, 1, 1, 1, 1},
       {1, 0, 1, 0, 0, 1, 0},
       {1, 1, 1, 1, 1, 1, 1},
       {1, 1, 1, 1, 0, 1, 1}
};

Then I defined 3 functions:

C#
public void DrawNumber(int n, Graphics g, Point pos)

Here, the maximal order of our Int32 number is found:

C#
while (true)
    if (n < Math.Pow(10, ++maxOrder))
        break;

Then, each single digit is found and called to be rendered:

C#
for (int ord = 0; ord < maxOrder; ord++)
{
    int dig = n / Convert.ToInt32(Math.Pow(10, ord)) % 10;

    Point digPos = new Point
        (pos.X + (maxOrder - ord - 1) * (size + space), pos.Y);

    DrawDigit(dig, g, digPos);
}

The next function:

C#
private void DrawDigit(int n, Graphics g, Point pos)

In this function, all single digits are called to be rendered:

C#
for (int i = 0; i < 7; i++)
    if (segValues[n, i] == 1)
        DrawSegment((Segment)i, g, pos);

And the last rendering function:

C#
private void DrawSegment(Segment s, Graphics g, Point pos)

Here I've defined all segments points (x, y) and rendered these segments, which are for actual digit enabled in variable segValues.

That's all, simple enough. The rest of the code is only handling of the application form.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Czech Republic Czech Republic
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionThis can be improved upon... Pin
Matt T Heffron7-Aug-15 13:53
professionalMatt T Heffron7-Aug-15 13:53 
There are several points of inefficiency in your implementation.
Here's what I'd change:
1. Don't use Math.Pow(10, ...).
Math.Pow handles the general case of non-integral exponent so it is significant computation.
An int has at most 10 decimal digits, so create a simple array of the digits, fill it at the same time as determining how many there are, and use that.
2. Precompute as many constant or stable values as possible. E.g., the offsets to the line segment endpoints.
3. Make segValues into bool instead of int.
(Yes, not a big deal... it's my preferred style.)
Like this:
C#
public class SegmentRender
{
  int size;       // digit width
  int lineWidth;  // line width
  int space;      // space between single digits
  Pen pen;        // pen used for drawing of segments

  private static bool[,] segValues =
  {
    { true,  true,  true, false,  true,  true,  true},
    {false, false,  true, false, false,  true, false},
    { true, false,  true,  true,  true, false,  true},
    { true, false,  true,  true, false,  true,  true},
    {false,  true,  true,  true, false,  true, false},
    { true,  true, false,  true, false,  true,  true},
    { true,  true, false,  true,  true,  true,  true},
    { true, false,  true, false, false,  true, false},
    { true,  true,  true,  true,  true,  true,  true},
    { true,  true,  true,  true, false,  true,  true}
  };

  private Size[] EndPointOffsets;
  private static readonly int[,] EndPointOffsetBase =
  {
    {0, 0},
    {1, 0},
    {0, 1},
    {1, 1},
    {0, 2},
    {1, 2},
  };

  private enum Segment
  {
    Up,
    LeftUp,
    RightUp,
    Middle,
    LeftDown,
    RightDown,
    Down
  }

  public SegmentRender(int size, int space, Color color, int lineWidth = 1)
  {
    this.size = size;
    this.space = space;
    this.lineWidth = lineWidth;
    pen = new Pen(color, lineWidth);
    // precompute these!
    int endPoints = EndPointOffsetBase.GetLength(0);
    EndPointOffsets = new Size[endPoints];
    for (int i = 0; i < endPoints; i++)
    {
      EndPointOffsets[i] = new Size(size * EndPointOffsetBase[i, 0], size * EndPointOffsetBase[i, 1]);
    }
  }

  // draw number by digital segments

  public void DrawNumber(int n, Graphics g, Point pos)
  {
    // we're ignoring negative numbers!
    if (n < 0)
      return;
    const int maxPossibleDigits = 10;   // for int (32 bit)
    int[] digits = new int[maxPossibleDigits];
    int digitPos = maxPossibleDigits;
    // collect the digits (from the low order end) at the same time as determining how many there are!
    do
    {
      int dig;
      n = Math.DivRem(n, 10, out dig);
      digits[--digitPos] = dig;
    } while (n > 0);

    // this loops through the digits in forward order
    int xoffset = 0;
    for (; digitPos < maxPossibleDigits; ++digitPos)
    {
      Point digPos = new Point(pos.X + xoffset, pos.Y);
      xoffset += size + space;
      DrawDigit(digits[digitPos], g, digPos);
    }
  }

  // draw single digit by digital segments

  private void DrawDigit(int n, Graphics g, Point pos)
  {
    for (Segment s = Segment.Up; s <= Segment.Down; ++s)
      if (segValues[n, (int)s])
        DrawSegment(s, g, pos);
  }

  // draw single segment

  private void DrawSegment(Segment s, Graphics g, Point pos)
  {
    switch (s)
    {
    case Segment.Up:
      g.DrawLine(pen, pos + EndPointOffsets[0], pos + EndPointOffsets[1]);
      break;
    case Segment.LeftUp:
      g.DrawLine(pen, pos + EndPointOffsets[0], pos + EndPointOffsets[2]);
      break;
    case Segment.RightUp:
      g.DrawLine(pen, pos + EndPointOffsets[1], pos + EndPointOffsets[3]);
      break;
    case Segment.Middle:
      g.DrawLine(pen, pos + EndPointOffsets[2], pos + EndPointOffsets[3]);
      break;
    case Segment.LeftDown:
      g.DrawLine(pen, pos + EndPointOffsets[2], pos + EndPointOffsets[4]);
      break;
    case Segment.RightDown:
      g.DrawLine(pen, pos + EndPointOffsets[3], pos + EndPointOffsets[5]);
      break;
    case Segment.Down:
      g.DrawLine(pen, pos + EndPointOffsets[4], pos + EndPointOffsets[5]);
      break;
    default:
      break;
    }
  }
}

"Fairy tales do not tell children the dragons exist. Children already know that dragons exist. Fairy tales tell children the dragons can be killed."
- G.K. Chesterton

AnswerRe: This can be improved upon... Pin
majaak9-Aug-15 8:54
majaak9-Aug-15 8:54 
GeneralRe: This can be improved upon... Pin
Matt T Heffron10-Aug-15 7:17
professionalMatt T Heffron10-Aug-15 7:17 
GeneralMy vote of 4 Pin
fredatcodeproject1-Aug-15 13:35
professionalfredatcodeproject1-Aug-15 13:35 
GeneralRe: My vote of 4 Pin
majaak2-Aug-15 22:54
majaak2-Aug-15 22:54 
GeneralRe: My vote of 4 Pin
fredatcodeproject3-Aug-15 4:34
professionalfredatcodeproject3-Aug-15 4:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.