Simulate a Seven-Segment Display in Flash (AS3)
Seven-segment displays are widely used in electronic and mechanical devices to display simple information such as the time (a digital clock) or a price (a local gas station's price per gallon). Advantages include ease of implementation, updating, and the most important: cost. With only 7 segments, such as a LED, you can display 10 different numerals (ie 0-9).
In the software design world, seven-segment displays can be useful because they are a fixed width, people recognize them and they have an interesting look. In this tutorial I will give explain the math behind a seven-segment display how to implement them in Flash ActionScript 3 (AS3).
What is a Seven-Segment Display?
Seven-segment displays (7-seg displays) are an effective and low-cost solution for displaying the numerals: 0,1,2,3,4,5,6,7,8,9. Multiple 7-seg displays placed in a row provides the ability to display larger numbers. With additional glyphs such as a colon more opportunites present themselves, such as displaying the time.
Segment displays come in a few different types including: Nine-segment, Fourteen-segment, and Sixteen-segment. Seven-segment are covered in this article. The decimal point usually included with segment displays will not be ignored. Also note that 7-segs are usually in italics (for readability and an interesting trick which requires multiple displays) but for simplicity no leans for my glpyhs.
The Segment
The building blocks of a segment display are its segments. The properties of a simple segment are:
- segment thickness (k): thickness of a segment
- segment length (l): length of a segment
- segment ratio (r): the segment thickness divided by the width k/l
- segment orientation (o): either horizontal or vertical
The Digit
Seven segments can create a digit. The segments are labeled "a" through "g" in a clockwise fashion:
The major variable which will effect our digit is the padding between segments defined as padding (p). However computer languages are based in the Cartesian coordinate system so we will need to convert this variable to a new variable which we will call: segmentPaddingCartesian (n).
To do this we focus on the space between our segment ends and find an isosceles right triangle.
Using a little trigonometry:
- n/p = sin(45°)
- n = sin(45°)*p
- n = (√2)/2*p
We can define segmentPaddingCartesian (n) as (√2)/2 multiplied by the padding (p).
The Glyphs
Although a seven-segment displays can display a number of glyphs our digit will only display the ten numerals: 0 - 9. The following truth table outlines which segments should be on or off to represent each glyph:
a | b | c | d | e | f | g | |
---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
2 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
3 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
4 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
5 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
6 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
7 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
8 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
9 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
Psudeocode
Before getting language specific here is the pseudocode:
createDigit() function createDigit() for each segment (A-G) if (segment width is greater than segment height) draw segment horizontal else draw segment vertical function updateDigit(glyph) if (glyph is not valid) throw error return else for each segment (A-G) if (glyph array with glyph index is true) make segment visible else make segment hidden
AS3 Implementation
The digit class is covered in this section. A download of the digit class source will follow at the end of this article.
First we need to define the state of each segment for each glyph. We achieve this through a multi-dimensional array called 'glyphs'. For example for the first glyph '0', all the segment states are true except the last segment.
glyphs[0] = new Array(true, true, true, true, true, true, false);
We continue the define the rest of the glyphs (1-9):
glyphs[1] = new Array(false, true, true, false, false, false, false); glyphs[2] = new Array(true, true, false, true, true, false, true); glyphs[3] = new Array(true, true, true, true, false, false, true); glyphs[4] = new Array(false, true, true, false, false, true, true); glyphs[5] = new Array(true, false, true, true, false, true, true); glyphs[6] = new Array(true, false, true, true, true, true, true); glyphs[7] = new Array(true, true, true, false, false, false, false); glyphs[8] = new Array(true, true, true, true, true, true, true); glyphs[9] = new Array(true, true, true, true, false, true, true);
Next we create a segment object for each segment. We do this now, because we are about to assign an angle to each segment.
// For each segment (a-g) var i:int = A; for(; i <= G + 1; i++) { segments[i] = new Object(); }
Now we set the angle of each segment to either 0 (horizontal) or 90 (vertical):
// Define each segment's angle segments[A].angle = 0; segments[B].angle = 90; segments[C].angle = 90; segments[D].angle = 0; segments[E].angle = 90; segments[F].angle = 90; segments[G].angle = 0;
For each segment we create a shape object so we can draw the graphics.
// For each segment (a-g) for(i = A; i <= G; i++) { segments[i].container = new Shape(); addChild(segments[i].container); }
Now that the object is created, we will need to update it as properties change (ie a change in the value or the height). Let's put it into a seperate method so we can call it when need it. The first part of this method updates properties.
// Update some variables that may have changed digitWidth = digitHeight / 2; segmentThickness = digitHeight * thicknessRatio; segmentPadding = digitHeight * paddingRatio; segmentLength = segmentThickness / segmentRatio; segmentPaddingCartesian = segmentPadding * Math.sqrt(2) / 2;
Since a property may have changed, we need to redefine each segment's position.
// Define each segment's position segments[A].x = segmentThickness / 2 + segmentPaddingCartesian; segments[A].y = 0; segments[B].x = segments[A].x + segmentLength - segmentThickness / 2 + segmentPaddingCartesian; segments[B].y = segments[A].x; segments[C].x = segments[B].x; segments[C].y = segments[B].y + segmentLength + 2 * segmentPaddingCartesian; segments[D].x = segments[A].x; segments[D].y = segments[C].y + segmentLength - segmentThickness / 2 + segmentPaddingCartesian; segments[E].x = 0; segments[E].y = segments[C].y; segments[F].x = 0; segments[F].y = segments[B].y; segments[G].x = segments[A].x; segments[G].y = segments[B].y + segmentLength - segmentThickness / 2 + segmentPaddingCartesian;
Now we position and draw each glpyh:
// For each segment (a-g) var i:int = A; for(; i <= G; i++) { // Position each segment segments[i].container.x = segments[i].x; segments[i].container.y = segments[i].y; // Draw the segment segments[i].container.graphics.clear(); segments[i].container.graphics.beginFill(segmentColor); if(!segments[i].angle) { segments[i].container.graphics.moveTo(0, segmentThickness / 2); segments[i].container.graphics.lineTo(segmentThickness / 2, 0); segments[i].container.graphics.lineTo(segmentLength - segmentThickness / 2, 0); segments[i].container.graphics.lineTo(segmentLength, segmentThickness / 2); segments[i].container.graphics.lineTo(segmentLength - segmentThickness / 2, segmentThickness); segments[i].container.graphics.lineTo(segmentThickness / 2, segmentThickness); segments[i].container.graphics.lineTo(0, segmentThickness / 2); } else { segments[i].container.graphics.moveTo(segmentThickness / 2, 0); segments[i].container.graphics.lineTo(0, segmentThickness / 2); segments[i].container.graphics.lineTo(0, segmentLength - segmentThickness / 2); segments[i].container.graphics.lineTo(segmentThickness / 2, segmentLength); segments[i].container.graphics.lineTo(segmentThickness, segmentLength - segmentThickness / 2); segments[i].container.graphics.lineTo(segmentThickness, segmentThickness / 2); segments[i].container.graphics.lineTo(segmentThickness / 2, 0); } segments[i].container.graphics.endFill(); }
The digit class includes a couple more methods (ie addParent and updateValue) and properties including color, value, and height. Hopefully these are self-explanatory.
AS3 Example
Selecting a choice from the combo box will set the value property of the digit object.
As an example a simple web interface that imports the digit class and provides a combo box to change the digit via the value method is made. Full documentation of the digit class can be found here.
The following code instantiates the digit object and sets some default properties.
import com.williammalone.Digit; var digit:Digit = new Digit(); digit.height = 100; digit.x = 240; digit.y = 20; digit.color = 0x557755; addChild(digit);
We create a combo box that will change the value properties of the digit object.
import fl.controls.ComboBox; var changeDigit:ComboBox = new ComboBox(); changeDigit.width = 120; changeDigit.x = 40; changeDigit.y = 10; changeDigit.prompt = "Select a digit"; changeDigit.addItem( { label: "0", data:0 } ); changeDigit.addItem( { label: "1", data:1 } ); changeDigit.addItem( { label: "2", data:2 } ); changeDigit.addItem( { label: "3", data:3 } ); changeDigit.addItem( { label: "4", data:4 } ); changeDigit.addItem( { label: "5", data:5 } ); changeDigit.addItem( { label: "6", data:6 } ); changeDigit.addItem( { label: "7", data:7 } ); changeDigit.addItem( { label: "8", data:8 } ); changeDigit.addItem( { label: "9", data:9 } ); addChild(changeDigit); changeDigit.addEventListener(Event.CHANGE, onDigitChanged); function onDigitChanged(e:Event):void { digit.value = changeDigit.selectedItem.data; }
AS3 Source
- Download Digit Class (Digit.as): digit_src-1_0.zip
- Download Example (SevenSegment.swf): SevenSegEx.zip
- Download Example Source (SevenSegment.fla): SevenSegEx_src.zip
The iPhone does not currently support downloads.
References
- Digit Class Documentation
- Seven Segment Display implemented in HTML5 and JavaScript
- Seven-Segment Displays
- Nine-Segment Displays
- Fourteen-Segment Displays
- Sixteen-Segment Displays
- Giant Seven-Segment Display