Demo Tutorial 2

Let’s get things moving shall we? In this tutorial I’m going to move that white dot we drew in the last tutorial around the screen!
The dot will follow coordinates generated by the sin and cos functions.

#include <math.h>
#include “ptc.h”

const SPEED=50;
const WIDTH=320;
const HEIGHT=200;

class Move_dot
{
  private:
    typedef struct count
    {
      int x;
      int y;
      int xdir;
      int ydir;
    } count;
    int32* sin_data;
    int32* cos_data;
    int frames;
    count current_pos;
    count last_pos;
    int dir;
    int last_counter;
  
    void update_counter (int* counter, int* dir)
    {
      if (dir[0] == 0) {
        if (counter[0] < 1024) {
          counter[0]++;
        } else {
          dir[0]=1;
        }
      } else {
        if (counter[0] > 0) {
          counter[0]–;
        } else {
          dir[0]=0;
        }
      }
    }
  public:
    Move_dot()
    {
      int index;
      sin_data = new int32[1024];
      cos_data = new int32[1024];
      for (float i=0; i < 360; i+=0.01) {
        index=(int)((i/360.0)*1024);
        sin_data[index]=(int)fabs(WIDTH*sin(i/-57.3));
        cos_data[index]=(int)fabs(HEIGHT*cos(i/-57.3));
      }
      frames=0;
      current_pos.x=150;
      current_pos.y=0;
      last_pos.x=150;
      last_pos.y=0;
      dir=0;
      last_counter=0;
    }

    ~Move_dot()
    {
      delete sin_data;
      delete cos_data;
    }

    void draw (int32 *buffer)
    {
        buffer[(int)((sin_data[last_pos.x])+(cos_data[last_pos.y]*WIDTH))]=0;
        buffer[(int)((sin_data[current_pos.x])+
          (cos_data[current_pos.y]*WIDTH))]=(255<<16)+(255<<8)+255;
        last_pos.x=current_pos.x;  
        last_pos.y=current_pos.y;  
        update_counter(&current_pos.x,&current_pos.xdir);
        update_counter(&current_pos.y,&current_pos.ydir);
        frames++;
    }
};
int main()
{
  try {
    int speed=SPEED;
    Move_dot move_dot;
The following is the Makefile you’ll use:

    // create format
    Format format(32,0x00FF0000,0x0000FF00,0x000000FF);

    // create console
    Console console;
    console.open(“tut02″,WIDTH,HEIGHT,format);

    // create surface
    Surface surface(WIDTH,HEIGHT,format);

    // loop until a key is pressed
    while (!console.key()) {

      if (speed == 0) {
        speed=SPEED;
        // lock surface
        int32 *buffer = (int32*) surface.lock();
  
        // draw dot
        move_dot.draw(buffer);
  
        // unlock surface
        surface.unlock();
  
        // copy to console
        surface.copy(console);
  
        // update console
        console.update();
      } else {
        speed–;
      }
    } // exit
    return 0;
  } catch (Error error) {
    // report error
    error.report();
  }
}

The following is the Makefile you’ll use:

# GNU Makefile for the X11 part of the PTC 2.0 C++ API
# Copyright (c) 1998 Christian Nentwich (brn@eleet.mcb.at)
# The PTC 2.0 C++ API is (c) 1998 Glenn Fiedler (ptc@gaffer.org)
# This package is licensed under the GNU LGPL
#
# Please refer to the file COPYING.LIB contained in the distribution for
# licensing conditions

include ../../Makefile.config

PROG1 = Tut02

all:  $(PROG1)

$(PROG1): $(PROG1).o
  $(CC) $(PROG1).o $(PTCLIB) $(PTC_X_LIBRARIES) -o $(PROG1)

.cc.o:
  $(CC) -c $(CFLAGS) $< clean:
  rm -f *.o

distclean:
  rm -f *.o
  rm -f $(PROG1)

Now compile Tut02.cc by typing make.
If you have previously compiled the example programs that came with PTC this should compile without problems.
Now run it from an xterm by typing:

./Tut02(return)
You should now see a window (or it will go fullscreen if your X server has such low resolutions) with a small white dot flying around it!
Press a key to escape out of the program.

How it works.

I’m not going to go back over the basic PTC stuff I covered in the previous tutorial. If you have any queries look at tutorial 1.

In my program I use 3 variables that never change. I defined these at the top of the program as constants:

const SPEED=50;
const WIDTH=320;
const HEIGHT=200;

The first constant, SPEED determines how often we refresh the screen. I have it set at 50 which works well on my machine. Change it and see what happens.
The last two figures should be self-explanatory. They are the width and height of the window we’ll create. These figures will also be used in generating our sin and cos values.
I used to wonder how I could code a demo in C++. “All those objects would get in the way”, I thought. Someone posted a message on CSIPD about a year or so ago voicing the same critisicm and among the replies was the idea that you put each demo part into a different class.
This is what I’ve done here. If I wanted to add a part to this small tutorial I would just code another class. With proper organisation I could create classes to do different functions of a demo part. Say, one class to move a scrolly and another class to draw and animate a logo or 3d object. All you would have to do is pass a pointer to the frame buffer to draw to!
“main” would probably take care of clearing the screen buffer at the end of each refresh, or another class could be called to record the changes to the frame buffer in-between calling each “action” class..
I’ve defined a Move_dot class here that contains all the code I need to draw and move my dot around the screen.
Inside the class I defined a struct to hold the details of the position of the dot as well as the direction (up or down) of the counter that runs through my sin and cos values. I also coded a small private function which either adds or subtracts 1 from the given counter and changes direction if necessary.
I decided to precalculate all my sin and cos values to speed up execution (precalc as much as possible!) and I needed two arrays to hold that data:

int32* sin_data;
int32* cos_data;

The class constructor, Move_dot(), creates my arrays for me, and fills the arrays with suitable values. It also defines various variables which I’ll use later on.
The class destructor, ~Move_dot(), on the other hand just deletes the two arrays freeing up the space they took.
The main function in the Move_dot class is the draw function.
It is called at every update to perform the animation on the white dot, and is the only public function in the class. It is called from the main loop of the program. It’s quite simple and should be easy to follow.

buffer[(int)((sin_data[last_pos.x])+(cos_data[last_pos.y]*WIDTH))]=0;

This line deletes the last drawn dot on the screen. We could just clear the whole buffer but this is much faster.

buffer[(int)((sin_data[current_pos.x])+(cos_data[current_pos.y]*WIDTH))]=(255< <16)+(255<<8)+255;

This line draws a white dot at some location on the screen.
The rest should be easy enough to decipher.
Mail me if you have problems.

Notes

Here’s a way to code demos in C++ which I touched upon above.
Code your demo parts as a series of classes. Each part should be contained in either one class or in a set of classes with suitable naming conventions.
i.e.

class part1_scroll
class part1_3dobj
class part1_music

class part2_vectors
class part2_dycp
class part2_music

If using the above you could set up PTC in “main” and create instances of each class and then call the draw function of each class. (or play of the music class.)
It’s probably advisable to let your “main” function clear the screen buffer after each refresh of video memory. Classes should just draw to the screen. They should not delete the previous image they drew (like my example above). This allows you to do nice things like implementing the “dirty rectangles” algorithm which just updates the parts of the screen which actually have been changed.
No, I’ve never done the above!


You might also like

If you like this post then please subscribe to my full RSS feed. You can also click here to subscribe by email. There are also my fabulous photos to explore too!

Leave a Reply

Loading Facebook Comments ...