import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.StringTokenizer; /* * ZazaMap2 * Brian Rudy (brudyNO@SPAMpraecogito.com) * Authored in NetBeans 3.2 IDE * Released under GPL (1.0) * * Grab images of current map, and robot. Scale to * dimensions of the applet panel. Get setup info * from position server CGI (mode, goal points). Get * robot's position and orientation from the position * server and draw as appropriate (server-push/client-pull?). * * Todo- Image auto-scaling, update frequency parameter * loading, exception handling!, server-side status reports, * scroll bars/popup window? * * --Revision History-- * * 0.46 8-15-2001 * Added basic goal pont voting. * * 0.45 8-2-2001 * Added basic target info window spawning. * * 0.44 7-31-2001 * Added stub for goal point selection. * * 0.43 7-30-2001 * Added basic goal plotting and labeling (currently disabled * for clarity). Fixed goal point parsing and array population. * Added getPos support. * * 0.42 7-28-2001 * Fixed exception handling in NetIO routines. Extended parsing. * Fixed robotImage scaling. * * 0.41 7-27-2001 * Added basic setup data retrieval and preliminary parsing. * * 0.4 7-25-2001 * Added basic NetIO framework * * 0.3 7-23-2001 * Basic image rendering using Java2D extensions for * image manipulation (rotation) * * Known Bugs- Java2D imagebuffer data not transparent when * pasted into 'g' canvas. Robot image has square borders * as a result */ /* * Moves a foreground image in front of a background image. */ public class ZazaMap2 extends javax.swing.JApplet implements java.awt.event.ActionListener { int frameNumber = -1; boolean frozen = false; javax.swing.Timer timer; ZazaMap2.AnimationPane animationPane; Image original; boolean isInitialized = false; // The first array must be initialized String[][] goals = new String[20][]; int numGoals; int PosX = 0; int PosY = 0; int PosTheta; // In degrees int goalSize = 10; URL targetURL; int goalSelected = 0; boolean asApplet = false; // These are only used when run as an application static String fgFile = "http://www.praecogito.com/~brudy/zaza/java/images/robot.gif"; static String bgFile = "http://www.praecogito.com/~brudy/zaza/java/images/map-in.gif"; //Invoked only when run as an applet. public void init() { asApplet = true; //Get the images. Image bgImage = getImage(getCodeBase(), getParameter("MAPIMAGE")); Image fgImage = getImage(getCodeBase(), getParameter("ROBOTIMAGE")); buildUI(getContentPane(), bgImage, fgImage); } void buildUI(java.awt.Container container, Image bgImage, Image fgImage) { //int fps = 10; int fps = 1; //How many milliseconds between frames? int delay = (fps > 0) ? (1000 / fps) : 100; //Set up a timer that calls this object's action handler. timer = new javax.swing.Timer(delay, this); timer.setInitialDelay(0); timer.setCoalesce(true); animationPane = new ZazaMap2.AnimationPane(bgImage, fgImage); container.add(animationPane, java.awt.BorderLayout.CENTER); animationPane.addMouseListener(new java.awt.event.MouseAdapter() { public void mousePressed(java.awt.event.MouseEvent e) { boolean clickedOnGoal = goalClick(e.getX(), e.getY(), goalSize); if (frozen) { frozen = false; startAnimation(); } else { if (!clickedOnGoal) { frozen = true; stopAnimation(); } } // on mouseUp event over goal, do something if (clickedOnGoal) { System.out.println("You selected goal number " + String.valueOf(goalSelected) + " which is " + goals[goalSelected][2]); // Cast your vote sendGoal(goalSelected); if (asApplet) { try { URL infoURL = new URL(goals[goalSelected][3]); getAppletContext().showDocument(infoURL, "infowindow"); } catch (MalformedURLException er) { } catch (IOException er) { } } } else { System.out.println("You clicked X=" + String.valueOf(e.getX()) + ", Y=" + String.valueOf(e.getY()) + " but there is nothing here."); } } }); } public boolean goalClick(int xposition, int yposition, int diameter) { if (isInitialized){ for(int i = 0; i < numGoals; i++){ if ((Integer.valueOf(goals[i][0]).intValue()-goalSize/2 <= xposition) && ((Integer.valueOf(goals[i][0]).intValue()-goalSize/2)+diameter >= xposition)){ if ((Integer.valueOf(goals[i][1]).intValue()-goalSize/2 <= yposition) && ((Integer.valueOf(goals[i][1]).intValue()-goalSize/2)+diameter >= yposition)){ goalSelected = i; return true; } } } } return false; } //Invoked by a browser only. public void start() { startAnimation(); } //Invoked by a browser only. public void stop() { stopAnimation(); } //Can be invoked from any thread. public synchronized void startAnimation() { if (frozen) { //Do nothing. The user has requested that we //stop changing the image. } else { //Start animating! if (!timer.isRunning()) { timer.start(); } } } //Can be invoked from any thread. public synchronized void stopAnimation() { //Stop the animating thread. if (timer.isRunning()) { timer.stop(); } } // This is called when the timer expires public void actionPerformed(java.awt.event.ActionEvent e) { //Advance animation frame. frameNumber++; // Get updated position info from posServer getPos(); //Display it. animationPane.repaint(); } // The following should be in it's own class String posServer = "http://www.praecogito.com/cgi-bin/posServer"; // Get setup info from posServer (goals) public void setup() { try { System.out.println("Getting setup info..."); String SetupStr= posServer + "?setup=yes"; System.out.println("I am getting " + SetupStr); URL url = new URL(SetupStr); URLConnection connection = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; StringTokenizer dataIn; String delimiter = ","; int tokenIndex; int index = 0; while ((inputLine = in.readLine()) != null) { tokenIndex = 0; // print it to the console System.out.println("Setup data: " + inputLine); // Parse with stringTokenizer dataIn = new StringTokenizer(inputLine, delimiter, true); goals[index] = new String[4]; //create sub-array // For goal parsing. Add x,y,name to goal array // x goals[index][0] = dataIn.nextToken(); // delimiter dataIn.nextToken(); // y goals[index][1] = dataIn.nextToken(); // delimiter dataIn.nextToken(); // name goals[index][2] = dataIn.nextToken(); // delimiter dataIn.nextToken(); // URL goals[index][3] = dataIn.nextToken(); // EOL delimiters dataIn.nextToken(); dataIn.nextToken(); index++; } numGoals = index; in.close(); isInitialized = true; } catch (MalformedURLException e) { } catch (IOException e) { } } // Get position info (x,y,theta) public void getPos() { try { System.out.println("Getting position info..."); String posReqStr= posServer + "?getpos=yes"; URL url = new URL(posReqStr); URLConnection connection = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; StringTokenizer dataIn; String delimiter = ","; while ((inputLine = in.readLine()) != null) { // print it to the console //System.out.println("Position data: " + inputLine); // Parse with stringTokenizer dataIn = new StringTokenizer(inputLine, delimiter, true); // x PosX = Integer.valueOf(dataIn.nextToken()).intValue(); // delimiter dataIn.nextToken(); // y PosY = Integer.valueOf(dataIn.nextToken()).intValue(); // delimiter dataIn.nextToken(); // name PosTheta = Integer.valueOf(dataIn.nextToken()).intValue(); // EOL delimiters dataIn.nextToken(); dataIn.nextToken(); } in.close(); //System.out.println("Current position x=" + String.valueOf(PosX) + ", y=" + String.valueOf(PosY) + ", theta=" + String.valueOf(PosTheta)); } catch (MalformedURLException e) { } catch (IOException e) { } } //Get status info (mode change, remote exceptions) public void getInfo() { try { System.out.println("Getting status info..."); String posReqStr= posServer + "?getinfo"; URL url = new URL(posReqStr); URLConnection connection = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { // print it to the console // Gotta parse this somehow System.out.println("Status info: " + inputLine); } in.close(); } catch (MalformedURLException e) { } catch (IOException e) { } } // Send goal vote (server-side tally/arbitration) public void sendGoal(int goalNum) { try { System.out.println("Sending goal number " + String.valueOf(goalNum)); String posReqStr = posServer + "?sendgoal=" + String.valueOf(goalNum); URL url = new URL(posReqStr); URLConnection connection = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { // print it to the console // Gotta parse this somehow System.out.println("Goal request reply: " + inputLine); } in.close(); } catch (MalformedURLException e) { } catch (IOException e) { } } class AnimationPane extends javax.swing.JPanel { Image background, foreground; private java.awt.image.BufferedImage bimg; public double angdeg; public int xPos, yPos; public AnimationPane(Image background, Image foreground) { this.background = background; this.foreground = foreground; } //Draw the current frame of animation. public void paintComponent(java.awt.Graphics g) { super.paintComponent(g); //paint any space not covered //by the background image int compWidth = getWidth(); int compHeight = getHeight(); int imageWidth, imageHeight; Color goalColor = Color.yellow; Color posColor = Color.red; Color goalTextColor = Color.blue; java.awt.Graphics2D g2 = null; //int fgWidth = foreground.getWidth(this); //int fgWidth = 20; final int fgWidth = 5; //int fgHeight = foreground.getHeight(this); //int fgHeight = 20; final int fgHeight = 5; if (bimg == null || bimg.getWidth() != fgWidth || bimg.getHeight() != fgHeight) { bimg = (java.awt.image.BufferedImage) createImage(fgWidth, fgHeight) ; System.out.print("I haven't a clue what I am doing, does it show?\n"); // Get setup info from server setup(); } g2 = bimg.createGraphics(); //g2.setBackground(getBackground()); //g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(java.awt.RenderingHints.KEY_RENDERING, java.awt.RenderingHints.VALUE_RENDER_QUALITY); //g2.clearRect(0, 0, fgWidth, fgHeight); //If we have a valid width and height for the //background image, draw it. imageWidth = background.getWidth(this); imageHeight = background.getHeight(this); if ((imageWidth > 0) && (imageHeight > 0)) { g.drawImage(background, (compWidth - imageWidth)/2, (compHeight - imageHeight)/2, this); } // Draw in goal points // We should probably do this once in an off-screen buffer // to make this more efficient... if (isInitialized) { // for each element in 1x3 array for (int i = 0; i < numGoals; i++) { int goalx = Integer.valueOf(goals[i][0]).intValue(); int goaly = Integer.valueOf(goals[i][1]).intValue(); //System.out.println("Plotting a goal at x=" + goals[i][0] //+ " y=" + goals[i][1] + " name=" + goals[i][2]); g.setColor(goalColor); g.fillOval(goalx - goalSize/2, goaly - goalSize/2, goalSize, goalSize); g.setColor(goalTextColor); // This works, but it clutters the canvas //g.drawString(goals[i][2], goalx, goaly-4); } } //If we have a valid width and height for the //foreground image, draw it. //imageWidth = foreground.getWidth(this); imageWidth = fgWidth; //imageHeight = foreground.getHeight(this); imageHeight = fgHeight; if ((imageWidth > 0) && (imageHeight > 0)) { //robot position info g.setColor(posColor); //String s = Double.toString(angdeg); //g.drawString("Theta =" + s, 2, background.getHeight(this)-4); g.drawString("Theta =" + String.valueOf(PosTheta), 2, background.getHeight(this)-4); // xPos = ((frameNumber*2) % (imageWidth + compWidth)) - imageWidth; // yPos = (compHeight - imageHeight)/2; // String x = Double.toString(xPos); // String y = Double.toString(yPos); //g.drawString("Robot_x=" + x, 2, background.getHeight(this)-14); g.drawString("Robot_x=" + String.valueOf(PosX), 2, background.getHeight(this)-14); //g.drawString("Robot_y=" + y, 2, background.getHeight(this)-24); g.drawString("Robot_y=" + String.valueOf(PosY), 2, background.getHeight(this)-24); //g2.rotate(Math.toRadians(angdeg), bimg.getWidth(this)/2, bimg.getHeight(this)/2); g2.rotate(Math.toRadians(-PosTheta), bimg.getWidth(this)/2, bimg.getHeight(this)/2); g2.translate(bimg.getWidth(this)/2-imageWidth/2,bimg.getHeight(this)/2-imageHeight/2); //g2.rotate(Math.toRadians(angdeg)); //g2.setBackground(getBackground()); //g2.clearRect(0, 0, fgWidth, fgHeight); // testing //g2.setColor(getBackground()); //g2.fillRect(0, 0, getWidth(), getHeight()); //g2.setColor(getForeground()); //setOpaque(true); //g2.setBackground(Color.blue); // This does not appear to have any effect // g2.setColor(Color.yellow); //g2.drawImage(background,0,0,xPos,yPos,this); g2.clearRect(0, 0, bimg.getWidth(this), bimg.getHeight(this)); g2.drawImage(foreground,0,0,imageWidth,imageHeight,this); angdeg += 2; if (angdeg == 360) { angdeg = 0; } g2.dispose(); //g.drawImage(bimg, xPos, yPos, this); g.drawImage(bimg, PosX - imageWidth/2, background.getHeight(this) - PosY - imageHeight/2, this); } } } //Invoked only when run as an application (which should only happen when testing) public static void main(String[] args) { //Image bgImage = java.awt.Toolkit.getDefaultToolkit().getImage(ZazaMap2.bgFile); //Image fgImage = java.awt.Toolkit.getDefaultToolkit().getImage(ZazaMap2.fgFile); try { URL bgURL = new URL(ZazaMap2.bgFile); URL fgURL = new URL(ZazaMap2.fgFile); Image bgImage = java.awt.Toolkit.getDefaultToolkit().getImage(bgURL); Image fgImage = java.awt.Toolkit.getDefaultToolkit().getImage(fgURL); javax.swing.JFrame f = new javax.swing.JFrame("ZazaMap2"); final ZazaMap2 controller = new ZazaMap2(); controller.buildUI(f.getContentPane(), bgImage, fgImage); f.addWindowListener(new java.awt.event.WindowAdapter() { public void windowIconified(java.awt.event.WindowEvent e) { controller.stopAnimation(); } public void windowDeiconified(java.awt.event.WindowEvent e) { controller.startAnimation(); } public void windowClosing(java.awt.event.WindowEvent e) { System.exit(0); } }); f.setSize(new java.awt.Dimension(365, 454)); f.setVisible(true); controller.startAnimation(); } catch (MalformedURLException e) { } catch (IOException e) { } } }