Wednesday, May 7, 2008

Summary of Development Process

Here is a summary of the development process for the Traffic Sim project:

I started by learning the basic drawing capabilities of Java2D, like how to draw a rectangle with certain dimensions in a certain location, etc. Once I got that stuff down, I created exact to-scale replicas of each road piece using these basic shapes, with their centers at 0,0. Eventually I was able to draw each piece to scale.

Once the basic graphics was implemented, I needed a way to put it all together. Therefore, I made java classes for each type of road, each with a draw function that would do the graphics stuff I had previously developed for that piece. Each class also had a way to internally store that piece's position, orientation, and size.

I also created a thing called an interface that acts like a generic roadway which each of the other classes implement. This allowed me to have the global system accept any type of roadway without having to specify a specific class.

After all of the pieces had classes, I needed a global picture that could draw many pieces in their correct position, orientation, and size. For this, I used things called Affine Transformations, one of which each piece's class contained. To draw a piece, I would first remember the original transformation of the global picture, then transform into the space of the piece I wanted to draw. Once the drawing was done, I would return to the original global space, which resulted in the piece being in its correct position, orientation, and size.

At this point, I could have a list of tokens and iterate through the list, drawing each token in its correct position on the global image. I then integrated the Webcam code into this, fetching the list of topcodes that the webcam read and transforming them into a list of token classes that could then be drawn. Each topcode had a position, orientation, and size that I used for the corresponding tokens.

Here I had a bit of an issue getting the webcam code and my code to work together, as the webcam code wasn't really designed to continuously give data to another part of the program. I got past this by having the webcam code run continuously in a different thread, and periodically asking it for its current topcode list.

Now the system could run continuously and keep an updated digital representation of a traffic system that the user had placed on the table. It was time to turn my attention to the cars. For this, I came up with a scheme where a car would have a set number of possible locations on each token, each with a hard-coded red rectangle that would be drawn at that location. Since the locations were relative to the tokens, the hard-coded values would move with the tokens as they were rotated and shifted around the table. The car would know which location it was at, and when it was time to move to the next location, it would simply update its current position variable and draw the rectangle for the new location.

Juno and I then started the incredibly tedious process of hard-coding graphics code for a rectangle at each possible position on each type of token. This took quite a while, but we got it done for nearly every token type. I now needed to implement the cars moving along the positions and drawing the rectangles as they went.

The first tricky part was handling when the car got to the end of a token and had to move to the next token. I had to create a network of connections, so that the connector of one token could tell the car what token it was connected to. I had the webcam read the locations of the connection points of each token, and if one was close enough to another, it would connect them. This meant creating a pointer from one to the other and back. Now, once a car got to the end of a token, it could ask the connector for the next token, set that to its current token, and set its position to 0. It would then move along in the next token until it got to the end, where it would repeat the process until it reached a connector which wasn't connected to another token.

The next tricky part was handling different possibilities for a car on a certain type of token. Depending on how a token was oriented and which direction the car or cars were coming from, the cars would have to go through the positions in different orders and be on different sides of the road. Fixing this was quite difficult and required some tricky math with various multiplication by -1 and different position offsets in each possible situation. Eventually I got it so the tokens could be placed in every possible way and the cars would drive in the correct direction and on the correct side of the road.

The final major programming tasks were making the system work with multiple cars at once (which created a whole additional bag of headaches) and getting the cars to move automatically with a timer, so that the user could press a button once and the cars would run through the system. Previously the user had to press a button for each car movement.

In addition to all the programming outlined above, Juno and I spent quite a bit of time trying to optimize the performance of the topcode reading, counter the interference the projector was causing, and align the projection with the physical objects. The performance of the topcode reading got a big boost by Mike and I putting a couple of hours into getting his custom webcam driver to work with our system, giving us full 2 megapixel resolution. I was able to solve the projector interference pretty well by using window tint film on the bottom of the glass and directly lighting the topcodes from below.

There were also countless smaller issues that would sidetrack me for a few hours here and there, but that was the major stuff. Hopefully this gives you a basic idea of my process throughout the extensive development that went into this project.

No comments: