Visualizing the Machine

It is very common to show how machines work by drawing simple pictures of the internal structure and talking about how things work inside. These pictures are made up of a few standard component shapes, interconnected by lines representing the wires used to pass signals from place to place.

After inventing that silly dance, I started thinking about how to better visualize the action in the machine, and came up with an idea for how we can see the action.

Adding Graphics

The first thing we need to do is set up a graphics linrary we can use for this visualization. Fortunately, I have a simple library that works on all platforms. The instructions on getting things set up are available in the appendix (see C++ Graphics Setup)

Visualizing Signal Movement

Here is a simple experiment that shows what I have in mind. The code uses the basic pattern needed to animate a dislay. Coordinate Points =================

We will be drawing lines on the screen using a simple X_Y coordinate ssystem. Here is a utility class that models a point in that system:

include/Point.h
1
2
3
4
5
6
7
8
9
#pragma once

class Point {
    public:
        Point();
        Point(int x, int y);
        int x;
        int y;
};

And the implementation:

src/Point.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include "Point.h"

Point::Point() {
    x = 0;
    y = 0;
}

Point::Point(int px, int py) {
    x = px;
    y = py;
}

Wires

We also need a simple class to mdel a wire:

include/Wire.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#pragma once

#include "Point.h"

class Wire {
    public:
        Wire();
        void addPoint(Point p);
        void draw(void);

        Point path[10];
        int numpoints;
};

And the implementation:

lib/Wire.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include "Wire.h"

Wire::Wire() {
    numpoints = 0;
}

void Wire::addPoint(Point p) {
    if(numpoints<10)
        path[numpoints++] = p;
}

void Wire::draw(void) {
}

Animation Routines

Most of the hard part in getting things to move is handled nicely in the Graphics library we are using. All we need to do is create a few procedures that deal with the display and keyboard:

DrawScene

This procedure draws a single screen, using data available in the collection of parts (and wires) we are using. FOr this demonstration, only wires are gong to be modeled.

To draw our simple system, we need to create a wire “path” that the signal will follow. We will define this path using a collection of point objects. Here is the code that sets this up:

Here is a chunk of code that we will use to draw “pins” at the end of each wire:

We will model the signal using a colored ball with the current signal value displays in hex notation. Here is a routint that will draw the signal:

Finally, here is the code to draw one static image of our simulation. This is the most important routine in the animation.

Animate

The animate routine makes changes in the data used for drawing a scene. In this example, all we need to do is update the position of a colored circle we are going to use to represent a signal moving along the wire. The coordinates of the wire need to be checked to make sure we update correctly.

KeyHandler

A final utility routine lets us use keys to control the action. All we will do here is use the “q” key to stop the program.

src/main.cpp
1
2
3
4
5
6
7
8
9
// keyboard handler to terminate the program when "q" key is pressed
void handleKey(unsigned char key, int x, int y) {
    switch(key) {
        case 'q':
        case 'Q':
            exit(0);
            break;
    }
}

That is all we need for this demonstration.

For easy reference, here is my complete main.cpp:

src/main.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#include "Graphics.h"
#define font GLUT_BITMAP_9_BY_15

#include "Point.h"
#include "Wire.h"

Point p1(80,20);
Point p2(130,20);
Point p3(130,70);
Point p4(180,70);
int wirelen = 4;

Wire w;

int pinwidth = 50;
int pinheight = 25;

int xSpeed = 1;
int ySpeed = 1;
int sCount = 0;
char val[] = "C3";

int xPos;
int yPos;
int vl = 9;
int vh = 5;
int signalradius = 12;
int pixel = 0;
int seg = -1;

void drawPin(int x, int y) {
    setColor(BLACK);
    drawBox(x-pinwidth/2,y-pinheight/2,x+pinwidth/2,y+pinheight/2);
}

void drawText(int x, int y, const char *string) {
	int j = strlen( string ); 
	glRasterPos2i( x - vl, y - vh );
	for( int i = 0; i < j; i++ ) {
		glutBitmapCharacter(font, string[i]);
	}
}
void drawScene(void) {
    clearWindow();          // clear the last scene
    drawPin(w.path[0].x-pinwidth/2,w.path[0].y);
    setColor(BLACK);
    for(int i = 0; i<3;i++) {
        drawLine(w.path[i].x,w.path[i].y ,w.path[i+1].x, w.path[i+1].y);    // 20 steps
    }
    drawPin(w.path[3].x+pinwidth/2,w.path[3].y);
    setColor(RED);
    drawFilledCircle(xPos, yPos, signalradius);
    setColor(WHITE);
    drawText(xPos,yPos,"C3");
    sCount++;
    glutSwapBuffers();      // double buffering control
}

void animate() {
    // move your objects here
    if(pixel < 150) {
        if(pixel % 50 == 0) seg++;
        if(seg == 1)
            yPos += ySpeed;
        else
            xPos += xSpeed;
    }
    glutPostRedisplay();
    pause_ms(16);
    pixel++;
}

// keyboard handler to terminate the program when "q" key is pressed
void handleKey(unsigned char key, int x, int y) {
    switch(key) {
        case 'q':
        case 'Q':
            exit(0);
            break;
    }
}

void builder(void) {
    w.addPoint(p1);
    w.addPoint(p2);
    w.addPoint(p3);
    w.addPoint(p4);
    xPos = w.path[0].x;
    yPos = w.path[0].y;

}

int main(int argc, char **argv) {
    builder();
    graphicsSetup(argc, argv);      // initialize the graphics system
    glutDisplayFunc(drawScene);     // tell GLUT what function draws the scene
    glutIdleFunc(animate);          // Move objects when animating
    glutKeyboardFunc(handleKey);    // set up the "q" key to quit
    glutMainLoop();                 // GLUT will control the action
    //glutSwapBuffers();              // double buffering control
}

And the two class implementation files:

lib/Point.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include "Point.h"

Point::Point() {
    x = 0;
    y = 0;
}

Point::Point(int px, int py) {
    x = px;
    y = py;
}
lib/Wire.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include "Wire.h"

Wire::Wire() {
    numpoints = 0;
}

void Wire::addPoint(Point p) {
    if(numpoints<10)
        path[numpoints++] = p;
}

void Wire::draw(void) {
}