Chapter 2- Visualizing C programs
In this chapter you will see two step-by-step examples showing how to use Leonardo to visualize C programs. Chapters 9, 10, 11 and 12 will give you more detailed information about these topics.
2.1 - Leonardo's approach to visualization
The declarations are expressed in a logic programming language named Alpha and are called predicates. The syntax of Alpha and the computation mechanism of Alpha predicates are described in Chapter 9.
Leonardo's visualization system is able to recognize a set of standard predicates, concerning the existence of graphical objects and their retinal features, and to compute them in order to retrieve information about what has to be visualized. The heads of standard predicates are predefined and the user interested in realizing a new animation has only to fill-in their bodies according to the program requirements.
In other words, standard predicates are similar to the main function of the C language, that is, a special function recognized and automatically called by the environment. Due to their multiplicity, they are hierarchically organized according to their logical dependencies: for instance, it does not make sense for the user to define the color of an object which does not exist.
Standard predicates can be classified as: (a) animation control predicates (e.g., InferenceOn), (b) enumerative predicates defining objects and their structural properties (e.g., Line or Graph), and (c) predicates for attributes and retinal properties (e.g., LineThickness or NodeColor).
In order to use standard predicates, nothing special has to be done by Leonardo's users, as standard predicates are automatically recognized by Leonardo's visualization system.
However, in addition to standard predicates, Leonardo provides also library predicates, that are built on the top of the standard ones and have more specialized functions. For example, a library (named ProgBarLib.c) for visualizing the progress bar of running programs is available.
Libraries have to be included in your C project in order to make related library predicates work. For instance, if you want to use the ProgressBar library predicate, you should include ProgBarLib.c in your C project, or your declarations will have no effect. Libraries are available in Leonardo Folder:C Libraries.
Leonardo's standard and library predicates are listed in Appendix A. A detailed description of each predicate, classified according to its target, is given in Appendix A1, Appendix A2, Appendix A3, Appendix A4, and Appendix A5.
2.2 - Visualization of the sorting algorithm
|
#include <stdio.h> int n; long v[100]; void BubbleSort(long v[],int n){
do {
for (i=1;i<n;i++)
v[i-1]=v[i]; v[i]=temp; another=1; } } while (another); } |
void main(){
printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) {
scanf("%ld",&v[i]); } BubbleSort(v,n); for (i=0;i<n;i++)
} |
Step 1. Choosing the information to visualize
In our example concerning bubblesort, we aim at creating the classical sticks visualization for sorting algorithms: the numbers to be sorted are depicted as an ordered set of sticks with heights proportional to their values. An example of this kind of visualization is shown in the following image.
Step 2. Identifying the meaningful data structures
In our example, the meaningful data structures are the following:
We will refer to variables n and v for declaring the rectangles associated to the values to be sorted. On the other side, variable i will be used for coloring the rectangles corresponding to items being swapped during the execution of the BubbleSort function.
Step 3. Creating a basic visualization
The following piece of code accomplishes this task. Paste this piece of code in Leonardo's text editor window as follows:
|
#include <stdio.h>
int n; long v[100];
/** #define HSpace 8 #define Width 12 #define Middle 150 #define Dy 4
View(Out 0); Rectangle(Out N,Out X,Out Y,Out L,Out H,0)
**/
void BubbleSort(long v[],int n){
|
for (i=1;i<n;i++)
v[i-1]=v[i]; v[i]=temp; another=1; } } while (another); } void main(){
printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) {
scanf("%ld",&v[i]); } BubbleSort(v,n); for (i=0;i<n;i++)
} |
The choice of enclosing visualization predicates in special comments guarantees the reusability of your C source code, that can still be compiled in programming environments other than Leonardo, even if it has been annotated with visualization declarations.
For what concerns the visualization, note that four constants useful for defining the geometric layout of rectangles are defined:
All constants indicate a number of pixels. Their meaning is illustrated in the next figure:
Two standard enumeration predicates have been used for realizing the visualization: View and Rectangle.
For what concerns the implementation of Rectangle, observe that the enumeration statement introduced by the keyword For is used for enumerating, one by one, all the numbers in the range [0..n-1]. In particular, this is done by repeatedly calling a predefined predicate named InRange.
Then, after the identification number N of a rectangle has been obtained, its geometrical features are assigned. Namely, its length L is a predefined constant, and its height H is proportional to the value in v corresponding to the rectangle itself. The x-coordinate X of the left-top corner is proportional to the identification number of the rectangle, since rectangle N must be assigned with the N-th position. At last, the y-coordinate Y of the left-top corner is assigned as follows: half of the height of the rectangle is subtracted from the position of an imaginary horizontal axis along which all the rectangles must be aligned (the subtraction is due to the fact the the y-axis grows from top to bottom).
At this point, if you run the program, the execution environment should appear as follows:
Note that a view is automatically opened as a consequence of the declaration View(Out 0). The name of the view contains the name of the project, i.e. BubbleSort, followed by the keyword View and by the identification number, i.e. 0.
At the very beginning of the execution, the view contains no rectangle since both the value of variable n and the values in array v are equal to 0.
Now, you can insert the input as requested by the program. We suggest to start with 6 numbers (i.e. n=6) chosen as follows: 50, 30, 60, 20, 40, 10. Note that, each time a new value is assigned to array v, the corresponding rectangle automatically appears in the view.
Finally, observe that during the execution of the BubbleSort function, all the swaps between items in v are automatically detected by Leonardo's visualization system. Therefore, you will see the rectangles whose positions are being exchanged that grow/shrink while they are being swapped.
Step 4. Refining the visualization: adding colors to rectangles
We can accomplish this task using the predicate RectangleColor.
In particular, in the global scope we declare that all rectangles are grey using the following declaration:
|
/** Set 1: RectangleColor(N,Out Grey,0); **/ |
The directive Set is used for grouping a set of predicates into a set and for assigning them with a numeric label. In our example, the label 1 is associated to the single declaration RectangleColor(N, Out Grey, 0). In this way, in the following we will be able to refer to this declaration using its label for making it inactive and possibly for substituting it with more appropriate visualization rules.
Indeed, at the very beginning of the BubbleSort function, we can write the following:
|
/**
RectangleColor(N,Out C,0)
**/ |
We substitute it with another declaration, i.e. RectangleColor(N, Out C, 0) Assign C= (N==i) ? Red : (N==i-1) ? Green : Grey, that means that the rectangle with identification number equal to i is Red, the rectangle with identification number equal to i-1 is Green, and all the remaining rectangles are Grey.
The complete code obtained so far is shown below.
|
#include <stdio.h> int n; long v[100];
/** #define HSpace 8 #define Width 12 #define Middle 150 #define Dy 4
View(Out 0); Rectangle(Out N,Out X,Out Y,Out L,Out H,0)
Set 1: RectangleColor(N,Out Grey,0); **/
void BubbleSort(long v[],int n){
/** Negate 1; RectangleColor(N,Out C,0)
**/ |
for (i=1;i<n;i++)
v[i-1]=v[i]; v[i]=temp; another=1; } } while (another); }
void main(){
printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) {
scanf("%ld",&v[i]); } BubbleSort(v,n); for (i=0;i<n;i++)
} |
In general, the visualization predicates have the same visibility rules as C variables: each predicate is active only within the scope where it has been declared.
Step 5. Refining the visualization: skipping visualization events
Indeed, the swapping of two items in the BubbleSort function is performed by means of three consecutive assignment statements. First, the value of item v[i-1] is stored in the auxiliary variable temp; then, item v[i-1] is assigned with the value of item v[i]; finally, item v[i] is assigned with the value of the auxiliary variable temp.
The second and the third assignment statements are interesting from a visualization point of view, since they change the content of array v that is referred to by predicate Rectangle.
Their execution produces the following visual effect: first, the green rectangle assumes the same height as the red rectangle, then the height of the red rectangle increases and becomes equal to the previous height of the green rectangle.
However, this effect is not desired, as we would like to consider the swap operation as an atomic step. We can use the animation control predicate VisualUpdateOn to do this. In particular, you can paste the Alpha declarations /** Not VisualUpdateOn; **/ and /** VisualUpdateOn; **/ in the C source code as follows:
|
#include <stdio.h> int n; long v[100];
/** #define HSpace 8 #define Width 12 #define Middle 150 #define Dy 4
View(Out 0); Rectangle(Out N,Out X,Out Y,Out L,Out H,0)
Set 1: RectangleColor(N,Out Grey,0); **/
void BubbleSort(long v[],int n){
/** Negate 1; RectangleColor(N,Out C,0)
**/ |
for (i=1;i<n;i++)
/** Not VisualUpdateOn; **/ v[i-1]=v[i]; v[i]=temp; /** VisualUpdateOn; **/ another=1; } } while (another); }
void main(){
printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) {
scanf("%ld",&v[i]); } BubbleSort(v,n); for (i=0;i<n;i++)
} |
Step 6. Refining the visualization: designing a more robust geometric layout
First of all, remember that rectangles are currently centered along a horizontal axis whose vertical position is fixed and equal to the constant Middle. The solution to the layout problem shown so far consists in not keeping this position fixed, but in making it change according to the maximum value in array v. In this way, the visualization of all rectangles will always fill into both the left and the top side of the window.
With this aim, it is useful to define an auxiliary (non-standard) predicate that can be used to make the implementation of predicate Rectangle more readable. Note that auxiliary predicates play for Alpha programs the same role that macros and subroutines have for C programs.
We call the auxiliary predicate Max and we define it as follows:
|
/**
**/ |
The previous declaration can be inserted in the global scope, just after the declarations of C variables n and v. Indeed, only these two variables are referred to by predicate Max.
At this point, we can modify the implementation of predicate Rectangle as follows:
|
Rectangle(Out N,Out X,Out Y,Out L,Out H,0)
|
The complete visualization code is shown below.
|
#include <stdio.h> int n; long v[100];
/** #define HSpace 8 #define Width 12 #define Dy 4
Max(Out M) Assign M In {
};
View(Out 0); Rectangle(Out N,Out X,Out Y,Out L,Out H,0)
Set 1: RectangleColor(N,Out Grey,0); **/
void BubbleSort(long v[],int n){
/** Negate 1; |
**/ do {
for (i=1;i<n;i++)
/** Not VisualUpdateOn; **/ v[i-1]=v[i]; v[i]=temp; /** VisualUpdateOn; **/ another=1; } } while (another); }
void main(){
printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) {
scanf("%ld",&v[i]); } BubbleSort(v,n); for (i=0;i<n;i++)
} |
2.3 - Another visualization example: the breadth-first visit of a graph
Sorry, this example is still under construction.