root / trunk / code / projects / colonet / ColonetGUI / Colonet.java @ 38
History | View | Annotate | Download (21.6 KB)
1 | 32 | gtress | //
|
---|---|---|---|
2 | // Colonet.java
|
||
3 | //
|
||
4 | |||
5 | import javax.swing.*; |
||
6 | import java.awt.*; |
||
7 | import java.awt.image.*; |
||
8 | import java.awt.event.*; |
||
9 | import java.net.*; |
||
10 | import java.io.*; |
||
11 | import java.util.Random; |
||
12 | import java.applet.*; |
||
13 | |||
14 | public class Colonet extends JApplet implements ActionListener, MouseListener, Runnable { |
||
15 | |||
16 | final int CANVAS_SIZE = 500; //don't make this too large, or the applet will be slow. |
||
17 | final int BUFFER = 50; |
||
18 | final int RADIUS = 30; |
||
19 | |||
20 | // Connection
|
||
21 | JTextField txtHost;
|
||
22 | JTextField txtPort;
|
||
23 | JButton btnConnect;
|
||
24 | JButton btnGraph;
|
||
25 | JLabel lblConnectionStatus;
|
||
26 | JTextArea txtInput;
|
||
27 | JTextArea txtInfo;
|
||
28 | JPanel panelConnect;
|
||
29 | JPanel panelServerInterface;
|
||
30 | |||
31 | // Stats
|
||
32 | JLabel lblBattery;
|
||
33 | JLabel lblTokenPasses;
|
||
34 | JLabel lblHastToken;
|
||
35 | JPanel panelStats;
|
||
36 | |||
37 | // South
|
||
38 | JPanel panelSouth;
|
||
39 | JTextArea log;
|
||
40 | JScrollPane spLog;
|
||
41 | |||
42 | // Control
|
||
43 | JPanel panelControl;
|
||
44 | JTabbedPane tabPaneControl;
|
||
45 | JPanel panelRobotControl;
|
||
46 | JPanel panelRobotDirection;
|
||
47 | JPanel panelRobotCommands;
|
||
48 | JButton btnF, btnB, btnL, btnR, btnActivate;
|
||
49 | |||
50 | // Task Manager
|
||
51 | JPanel panelTaskManager;
|
||
52 | JScrollPane spTaskManager;
|
||
53 | JPanel panelTaskManagerControls;
|
||
54 | JPanel panelTaskManagerControlsPriority;
|
||
55 | DefaultListModel taskListModel;
|
||
56 | JList taskList;
|
||
57 | JButton btnAddTask;
|
||
58 | JButton btnRemoveTask;
|
||
59 | JButton btnMoveTaskUp;
|
||
60 | JButton btnMoveTaskDown;
|
||
61 | |||
62 | // Graphics
|
||
63 | JPanel panel;
|
||
64 | GraphicsConfiguration gc;
|
||
65 | volatile BufferedImage image; |
||
66 | volatile Graphics2D canvas; |
||
67 | int cx, cy;
|
||
68 | |||
69 | Socket socket;
|
||
70 | OutputStreamWriter out; //TODO: add a BufferedWriter |
||
71 | DataListener datalistener; |
||
72 | |||
73 | Font botFont;
|
||
74 | Random random = new Random(); |
||
75 | volatile int tokenLoc; //the token is currently here |
||
76 | volatile int numBots; |
||
77 | volatile int selectedBot; //the user has selected this bot |
||
78 | volatile Rectangle[] botRect; //contains boundary shapes around bots for click detection |
||
79 | |||
80 | Thread drawThread;
|
||
81 | SelectionIndicator indicator; |
||
82 | PacketMonitor packetMonitor; |
||
83 | 35 | gtress | ColonetServerInterface csi; |
84 | 32 | gtress | |
85 | |||
86 | public void init () { |
||
87 | // set the default look and feel
|
||
88 | String laf = UIManager.getSystemLookAndFeelClassName(); |
||
89 | try {
|
||
90 | UIManager.setLookAndFeel(laf);
|
||
91 | } catch (UnsupportedLookAndFeelException exc) { |
||
92 | System.err.println ("Warning: UnsupportedLookAndFeel: " + laf); |
||
93 | } catch (Exception exc) { |
||
94 | System.err.println ("Error loading " + laf + ": " + exc); |
||
95 | } |
||
96 | // We should invoke and wait to avoid browser display difficulties
|
||
97 | Runnable r = new Runnable() { |
||
98 | public void run() { |
||
99 | createAndShowGUI(); |
||
100 | } |
||
101 | }; |
||
102 | try {
|
||
103 | SwingUtilities.invokeAndWait(r);
|
||
104 | } catch (InterruptedException e) { |
||
105 | //Not really sure why we would be in this situation
|
||
106 | System.out.println(e);
|
||
107 | } catch (java.lang.reflect.InvocationTargetException e) {
|
||
108 | //This should never happen. Seriously.
|
||
109 | System.out.println(e);
|
||
110 | } |
||
111 | } |
||
112 | |||
113 | public void destroy () { |
||
114 | try { drawThread.interrupt(); } catch (Exception e) { } |
||
115 | try { indicator.interrupt(); } catch (Exception e) { } |
||
116 | try { packetMonitor.interrupt(); } catch (Exception e) { } |
||
117 | } |
||
118 | |||
119 | private synchronized void createAndShowGUI () { |
||
120 | // init graphical elements
|
||
121 | panel = new JPanel(false); //set automatic double-buffering to false. we are doing it manually. |
||
122 | |||
123 | // Connection area
|
||
124 | txtInput = new JTextArea("- 9 3 - 1\n- - - 5 -\n4 - - - 2\n- - - - -\n1 - - 3 -"); |
||
125 | txtInput.setBorder(BorderFactory.createTitledBorder("Input Matrix")); |
||
126 | txtInfo = new JTextArea(); |
||
127 | txtInfo.setBorder(BorderFactory.createTitledBorder("Info")); |
||
128 | txtInfo.setEditable(false);
|
||
129 | btnGraph = new JButton("Run"); |
||
130 | txtHost = new JTextField("roboclub1.frc.ri.cmu.edu"); |
||
131 | txtHost.setBorder(BorderFactory.createTitledBorder("Host")); |
||
132 | txtPort = new JTextField("10123"); |
||
133 | txtPort.setBorder(BorderFactory.createTitledBorder("Port")); |
||
134 | btnConnect = new JButton("Connect"); |
||
135 | lblConnectionStatus = new JLabel("Status: Offline"); |
||
136 | panelConnect = new JPanel(); |
||
137 | panelConnect.setLayout(new GridLayout(6,1)); |
||
138 | panelConnect.add(lblConnectionStatus); |
||
139 | panelConnect.add(txtHost); |
||
140 | panelConnect.add(txtPort); |
||
141 | panelConnect.add(btnConnect); |
||
142 | panelConnect.add(txtInfo); |
||
143 | panelConnect.add(btnGraph); |
||
144 | panelServerInterface = new JPanel(); |
||
145 | panelServerInterface.setLayout(new GridLayout(2,1)); |
||
146 | panelServerInterface.add(panelConnect); |
||
147 | panelServerInterface.add(txtInput); |
||
148 | |||
149 | // Status Elements
|
||
150 | lblTokenPasses = new JLabel(); |
||
151 | lblBattery = new JLabel("???"); |
||
152 | panelStats = new JPanel(); |
||
153 | panelStats.setLayout(new GridLayout(4,2)); |
||
154 | panelStats.add(new JLabel("Token Passes / sec ")); |
||
155 | panelStats.add(lblTokenPasses); |
||
156 | panelStats.add(new JLabel("Battery ")); |
||
157 | panelStats.add(lblBattery); |
||
158 | panelStats.add(new JLabel("Token Passes / sec ")); |
||
159 | panelStats.add(lblTokenPasses); |
||
160 | |||
161 | //TODO: add panelStats somewhere!
|
||
162 | |||
163 | // Robot direction panel
|
||
164 | panelRobotDirection = new JPanel(); |
||
165 | btnF = new JButton("^"); |
||
166 | btnB = new JButton("v"); |
||
167 | btnL = new JButton("<"); |
||
168 | btnR = new JButton(">"); |
||
169 | btnActivate = new JButton("o"); |
||
170 | panelRobotDirection = new JPanel(); |
||
171 | panelRobotDirection.setLayout(new GridLayout(3,3)); |
||
172 | panelRobotDirection.add(new JLabel("")); |
||
173 | panelRobotDirection.add(btnF); |
||
174 | panelRobotDirection.add(new JLabel("")); |
||
175 | panelRobotDirection.add(btnL); |
||
176 | panelRobotDirection.add(btnActivate); |
||
177 | panelRobotDirection.add(btnR); |
||
178 | panelRobotDirection.add(new JLabel("")); |
||
179 | panelRobotDirection.add(btnB); |
||
180 | panelRobotDirection.add(new JLabel("")); |
||
181 | |||
182 | // Robot Control and Commands
|
||
183 | panelRobotCommands = new JPanel(); |
||
184 | panelRobotCommands.setLayout(new FlowLayout()); |
||
185 | panelRobotCommands.add(new JLabel("Commands go here")); |
||
186 | panelRobotControl = new JPanel(); |
||
187 | panelRobotControl.setLayout(new GridLayout(2,1)); |
||
188 | panelRobotControl.add(panelRobotDirection); |
||
189 | panelRobotControl.add(panelRobotCommands); |
||
190 | |||
191 | // Task Manager
|
||
192 | panelTaskManager = new JPanel(); |
||
193 | panelTaskManager.setLayout(new BorderLayout()); |
||
194 | taskListModel = new DefaultListModel(); |
||
195 | taskListModel.addElement("Map the Environment");
|
||
196 | taskListModel.addElement("Clean Up Chemical Spill");
|
||
197 | taskListModel.addElement("Grow Plants");
|
||
198 | taskListModel.addElement("Save the Cheerleader");
|
||
199 | taskListModel.addElement("Save the World");
|
||
200 | taskList = new JList(taskListModel); |
||
201 | taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||
202 | taskList.setSelectedIndex(0);
|
||
203 | spTaskManager = new JScrollPane(taskList); |
||
204 | panelTaskManagerControls = new JPanel(); |
||
205 | panelTaskManagerControls.setLayout(new GridLayout(1,3)); |
||
206 | panelTaskManagerControlsPriority = new JPanel(); |
||
207 | panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2)); |
||
208 | btnAddTask = new JButton("Add..."); |
||
209 | btnRemoveTask = new JButton("Remove"); |
||
210 | btnMoveTaskUp = new JButton("^"); |
||
211 | btnMoveTaskDown = new JButton("v"); |
||
212 | panelTaskManagerControlsPriority.add(btnMoveTaskUp); |
||
213 | panelTaskManagerControlsPriority.add(btnMoveTaskDown); |
||
214 | panelTaskManagerControls.add(btnAddTask); |
||
215 | panelTaskManagerControls.add(btnRemoveTask); |
||
216 | panelTaskManagerControls.add(panelTaskManagerControlsPriority); |
||
217 | panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
|
||
218 | panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
|
||
219 | panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH); |
||
220 | |||
221 | // Message log
|
||
222 | log = new JTextArea(); |
||
223 | spLog = new JScrollPane(log, |
||
224 | ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
|
||
225 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||
226 | spLog.setBorder(BorderFactory.createTitledBorder("Log")); |
||
227 | spLog.setPreferredSize(new Dimension(0, 150)); |
||
228 | log.setEditable(false);
|
||
229 | |||
230 | // Main control mechanism
|
||
231 | panelControl = new JPanel(); |
||
232 | panelControl.setLayout(new GridLayout(1,1)); |
||
233 | tabPaneControl = new JTabbedPane(JTabbedPane.TOP); |
||
234 | tabPaneControl.setPreferredSize(new Dimension(300, 0)); |
||
235 | tabPaneControl.addTab("Connection", panelServerInterface);
|
||
236 | tabPaneControl.addTab("Robots", panelRobotControl);
|
||
237 | tabPaneControl.addTab("Tasks", panelTaskManager);
|
||
238 | panelControl.add(tabPaneControl); |
||
239 | |||
240 | // Set up elements in the south
|
||
241 | panelSouth = new JPanel(); |
||
242 | panelSouth.setLayout(new GridLayout(1,2)); |
||
243 | panelSouth.add(spLog); |
||
244 | |||
245 | this.getContentPane().setLayout(new BorderLayout()); |
||
246 | this.getContentPane().add(panel, BorderLayout.CENTER); |
||
247 | this.getContentPane().add(panelSouth, BorderLayout.SOUTH); |
||
248 | this.getContentPane().add(panelControl, BorderLayout.EAST); |
||
249 | this.setVisible(true); |
||
250 | |||
251 | btnAddTask.addActionListener(this);
|
||
252 | btnRemoveTask.addActionListener(this);
|
||
253 | btnMoveTaskUp.addActionListener(this);
|
||
254 | btnMoveTaskDown.addActionListener(this);
|
||
255 | btnGraph.addActionListener(this);
|
||
256 | btnConnect.addActionListener(this);
|
||
257 | panel.addMouseListener(this);
|
||
258 | |||
259 | |||
260 | // Get the graphics configuration of the screen to create a buffer
|
||
261 | gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||
262 | .getDefaultScreenDevice().getDefaultConfiguration(); |
||
263 | image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE); |
||
264 | canvas = image.createGraphics(); |
||
265 | canvas.setStroke(new BasicStroke(2)); //set pen width |
||
266 | |||
267 | // Calculate center of canvas
|
||
268 | cx = image.getWidth() / 2;
|
||
269 | cy = image.getHeight() / 2;
|
||
270 | |||
271 | botFont = new Font("Arial", Font.PLAIN, 30); |
||
272 | tokenLoc = 0;
|
||
273 | numBots = 0;
|
||
274 | selectedBot = 0;
|
||
275 | |||
276 | // Set up dependent threads
|
||
277 | indicator = new SelectionIndicator(canvas);
|
||
278 | indicator.setRadius(RADIUS+3, 15); //a tad more than the bot radius |
||
279 | packetMonitor = new PacketMonitor();
|
||
280 | datalistener = new DataListener();
|
||
281 | 35 | gtress | |
282 | csi = new ColonetServerInterface(log);
|
||
283 | 32 | gtress | |
284 | } |
||
285 | |||
286 | 35 | gtress | public synchronized void paint (Graphics g) { |
287 | 32 | gtress | /* First, redraw the graphical components in the applet.
|
288 | This paint method overrides the built-in paint of the
|
||
289 | JApplet, and we don't want to deal with redrawing the
|
||
290 | components manually. Fuck that shit. */
|
||
291 | super.paint(g);
|
||
292 | |||
293 | // Place the buffered image on the screen, inside the panel
|
||
294 | panel.getGraphics().drawImage(image, 0, 0, Color.WHITE, this); |
||
295 | |||
296 | } |
||
297 | |||
298 | 35 | gtress | public synchronized void update (Graphics g) { |
299 | 32 | gtress | paint(g); |
300 | } |
||
301 | |||
302 | 35 | gtress | public void actionPerformed (ActionEvent e) { |
303 | 32 | gtress | Object source = e.getSource();
|
304 | if (source == btnGraph) {
|
||
305 | btnGraph.setEnabled(false);
|
||
306 | lblConnectionStatus.setText("Simulating");
|
||
307 | |||
308 | //Start dependent threads
|
||
309 | drawThread = new Thread(this, "drawThread"); |
||
310 | drawThread.start(); |
||
311 | indicator.start(); |
||
312 | packetMonitor.start(); |
||
313 | } else if (source == btnConnect) { |
||
314 | doSocket(); |
||
315 | } else if (source == btnRemoveTask) { |
||
316 | try {
|
||
317 | taskListModel.remove(taskList.getSelectedIndex()); |
||
318 | } catch (ArrayIndexOutOfBoundsException ex) { |
||
319 | } |
||
320 | } |
||
321 | } |
||
322 | |||
323 | 35 | gtress | private void randomize () { |
324 | 32 | gtress | Random r = new Random(); |
325 | StringBuilder s = new StringBuilder(); |
||
326 | |||
327 | int count = r.nextInt(8) + 1; |
||
328 | 35 | gtress | for (int i = 0; i < count; i++) { |
329 | for (int j = 0; j < count; j++) { |
||
330 | 32 | gtress | if (r.nextBoolean())
|
331 | s.append("" + (r.nextInt(16) + 1)); |
||
332 | else
|
||
333 | s.append("-");
|
||
334 | if (j != count-1) |
||
335 | s.append(" ");
|
||
336 | } |
||
337 | if (i != count-1) s.append("\n"); |
||
338 | } |
||
339 | |||
340 | txtInput.setText(s.toString()); |
||
341 | } |
||
342 | |||
343 | 35 | gtress | private void doSocket () { |
344 | csi.connect(txtHost.getText(), txtPort.getText()); |
||
345 | 32 | gtress | } |
346 | |||
347 | 35 | gtress | public void drawRobot (int id, int x, int y) { |
348 | 32 | gtress | //save the bot in memory, so we can tell if we click on it later
|
349 | botRect[id] = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS); |
||
350 | |||
351 | //draw the bot on the canvas
|
||
352 | canvas.setColor(Color.BLACK);
|
||
353 | canvas.drawOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2); |
||
354 | |||
355 | //draw the label
|
||
356 | canvas.setFont(botFont); |
||
357 | canvas.drawString("" + id, x-10, y+10); |
||
358 | } |
||
359 | |||
360 | 35 | gtress | public void drawConnection (int start, int end, int radius, Color color) { |
361 | 32 | gtress | final int ARROW_LENGTH = 18; |
362 | |||
363 | double angle = 2.0 * Math.PI / numBots; |
||
364 | int startx, starty, endx, endy;
|
||
365 | startx = cx - (int)(radius * Math.cos(start * angle)); |
||
366 | starty = cy - (int)(radius * Math.sin(start * angle)); |
||
367 | endx = cx - (int)(radius * Math.cos(end * angle)); |
||
368 | endy = cy - (int)(radius * Math.sin(end * angle)); |
||
369 | canvas.setColor(color); |
||
370 | canvas.drawLine(startx, starty, endx, endy); |
||
371 | |||
372 | //create arrow
|
||
373 | if (color.equals(Color.BLACK)) return; |
||
374 | int big_dy = starty - endy;
|
||
375 | int big_dx = endx - startx;
|
||
376 | double theta = 0; |
||
377 | if (big_dx == 0 && starty > endy) //pointing up |
||
378 | theta = Math.PI/2; |
||
379 | else if (big_dx == 0 && starty < endy) //pointing down |
||
380 | theta = 3*Math.PI/2; |
||
381 | else if (big_dy == 0 && startx > endx) //pointing left |
||
382 | theta = Math.PI;
|
||
383 | else if (big_dy == 0 && startx < endx) //pointing right |
||
384 | theta = 0;
|
||
385 | else
|
||
386 | theta = Math.atan(1.0 * big_dy / big_dx); |
||
387 | |||
388 | //create ploygon
|
||
389 | Polygon poly = new Polygon(); |
||
390 | int dx_arrow = Math.abs((int)(ARROW_LENGTH * Math.cos(theta))); |
||
391 | int dy_arrow = Math.abs((int)(ARROW_LENGTH * Math.sin(theta))); |
||
392 | int dy_half = (int)(ARROW_LENGTH/2 * Math.cos(theta)); |
||
393 | int dx_half = (int)(ARROW_LENGTH/2 * Math.sin(theta)); |
||
394 | int rx = (big_dx > 0) ? endx - dx_arrow : endx + dx_arrow; |
||
395 | int ry = (big_dy > 0) ? endy + dy_arrow : endy - dy_arrow; |
||
396 | poly.addPoint(endx, endy); |
||
397 | poly.addPoint(rx - dx_half, ry - dy_half); |
||
398 | poly.addPoint(rx + dx_half, ry + dy_half); |
||
399 | canvas.fillPolygon(poly); |
||
400 | } |
||
401 | |||
402 | 35 | gtress | public void run () { |
403 | while (true) { |
||
404 | 32 | gtress | step(); |
405 | repaint(); |
||
406 | 35 | gtress | try {
|
407 | Thread.sleep(90); |
||
408 | } catch (InterruptedException e) { |
||
409 | return;
|
||
410 | } |
||
411 | 32 | gtress | } |
412 | } |
||
413 | |||
414 | 35 | gtress | public void step () { |
415 | 32 | gtress | final int DIAMETER = image.getWidth() - 2*BUFFER; |
416 | final int BIGRADIUS = DIAMETER / 2; |
||
417 | final int TOKENRADIUS = 40; |
||
418 | boolean valid;
|
||
419 | |||
420 | // clear image
|
||
421 | canvas.setColor(Color.WHITE);
|
||
422 | canvas.fillRect(0, 0, image.getWidth(), image.getHeight()); |
||
423 | |||
424 | // parse the matrix, to see what robots exist
|
||
425 | String [] rows = txtInput.getText().split("\n"); |
||
426 | numBots = rows.length; |
||
427 | String [][] entries = new String[numBots][numBots]; |
||
428 | valid = true;
|
||
429 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
430 | 32 | gtress | entries[i] = rows[i].split(" ");
|
431 | if (entries[i].length != rows.length) valid = false; |
||
432 | } |
||
433 | |||
434 | 35 | gtress | if (valid) {
|
435 | 38 | gtress | this.showStatus("Running"); |
436 | 32 | gtress | |
437 | // draw robots and find which one is seleced
|
||
438 | double angle = 2.0 * Math.PI / numBots; |
||
439 | canvas.setColor(Color.BLACK);
|
||
440 | botRect = new Rectangle[numBots]; |
||
441 | int x, y;
|
||
442 | if (selectedBot >= numBots) selectedBot = 0; |
||
443 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
444 | 32 | gtress | x = cx - (int)(BIGRADIUS * Math.cos(i * angle)); |
445 | y = cy - (int)(BIGRADIUS * Math.sin(i * angle)); |
||
446 | drawRobot(i, x, y); |
||
447 | if (i == selectedBot) indicator.setCenter(x, y);
|
||
448 | } |
||
449 | |||
450 | // draw token marker
|
||
451 | int tokenx, tokeny;
|
||
452 | int tokenNum = tokenLoc;
|
||
453 | tokenx = cx - (int)(BIGRADIUS * Math.cos(tokenNum * angle)); |
||
454 | tokeny = cy - (int)(BIGRADIUS * Math.sin(tokenNum * angle)); |
||
455 | canvas.setColor(Color.RED);
|
||
456 | canvas.drawOval(tokenx-TOKENRADIUS, tokeny-TOKENRADIUS, 2*TOKENRADIUS, 2*TOKENRADIUS); |
||
457 | |||
458 | // create an inner circle along which the connections are made.
|
||
459 | // let the diameter of this circle be 2*RADIUS less than the outerDiameter.
|
||
460 | // see what connections exist
|
||
461 | 35 | gtress | for (int row = 0; row < numBots; row++) { |
462 | for(int col = 0; col < numBots; col++) { |
||
463 | if (!entries[row][col].equals("-") && entries[col][row].equals("-") && row != col) { |
||
464 | //TODO: Make a standard gray
|
||
465 | 32 | gtress | drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200)); |
466 | 38 | gtress | } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) { |
467 | 32 | gtress | drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
|
468 | } |
||
469 | } |
||
470 | } |
||
471 | |||
472 | // draw the selection indicator
|
||
473 | indicator.draw(); |
||
474 | |||
475 | 35 | gtress | } else {// if matrix is not valid |
476 | 32 | gtress | this.showStatus("Error: Invalid matrix"); |
477 | } |
||
478 | |||
479 | } |
||
480 | |||
481 | /* At this point, moveToken is only called by the simulator.
|
||
482 | * In the future, it can be rewritten to account for non-standard
|
||
483 | * token passing or deleted if the information can be retrieved
|
||
484 | * directly from the Colonet server instead.
|
||
485 | */
|
||
486 | 35 | gtress | public void moveToken () { |
487 | try {
|
||
488 | tokenLoc = (tokenLoc+1)%numBots;
|
||
489 | } catch (ArithmeticException e) { // in case numRobots is zero |
||
490 | } |
||
491 | 32 | gtress | |
492 | packetMonitor.addTokenPass(); |
||
493 | } |
||
494 | |||
495 | //
|
||
496 | // MouseEvent methods
|
||
497 | //
|
||
498 | public void mouseExited(MouseEvent e) {} |
||
499 | public void mouseEntered(MouseEvent e) {} |
||
500 | public void mouseReleased(MouseEvent e) {} |
||
501 | public void mouseClicked(MouseEvent e) {} |
||
502 | 35 | gtress | public void mousePressed(MouseEvent e) { |
503 | 32 | gtress | try {
|
504 | 35 | gtress | for (int i = 0; i < numBots; i++) { |
505 | 32 | gtress | if (botRect[i].contains(e.getPoint()))
|
506 | selectedBot = i; |
||
507 | } |
||
508 | } catch (Exception ex) { |
||
509 | System.out.println(e);
|
||
510 | } |
||
511 | |||
512 | } |
||
513 | |||
514 | /*
|
||
515 | * SelectionIndicator thread.
|
||
516 | * Graphical representation of the selection marker
|
||
517 | *
|
||
518 | * step() and draw() are synchronized methods. step() is private and
|
||
519 | * used to update the position of the crosshairs. draw() is called
|
||
520 | * externally and should only run if all calculations in step() have
|
||
521 | * been completed.
|
||
522 | */
|
||
523 | 35 | gtress | private class SelectionIndicator extends Thread { |
524 | 32 | gtress | |
525 | final int INDICATOR_DELAY = 100; |
||
526 | final double DTHETA = 0.3; //larger values make the marker rotate faster |
||
527 | Graphics2D g; //canvas to draw on |
||
528 | boolean running;
|
||
529 | |||
530 | int sx, sy; //center |
||
531 | int r, dr; //radius and width of marker |
||
532 | double theta; //current angle |
||
533 | |||
534 | volatile Polygon poly1, poly2, poly3, poly4; |
||
535 | |||
536 | int px1, py1;
|
||
537 | int rx1, ry1;
|
||
538 | int px2, py2;
|
||
539 | int rx2, ry2;
|
||
540 | int px3, py3;
|
||
541 | int rx3, ry3;
|
||
542 | int px4, py4;
|
||
543 | int rx4, ry4;
|
||
544 | |||
545 | int steps;
|
||
546 | |||
547 | 35 | gtress | public SelectionIndicator (Graphics2D g) { |
548 | 32 | gtress | super("SelectionIndicator"); |
549 | this.g = g;
|
||
550 | running = false;
|
||
551 | steps = 0;
|
||
552 | |||
553 | theta = 0;
|
||
554 | rx1 = 0; ry1 = 0; |
||
555 | px1 = 0; py1 = 0; |
||
556 | rx2 = 0; ry2 = 0; |
||
557 | px2 = 0; py2 = 0; |
||
558 | rx3 = 0; ry3 = 0; |
||
559 | px3 = 0; py3 = 0; |
||
560 | rx4 = 0; ry4 = 0; |
||
561 | px4 = 0; py4 = 0; |
||
562 | } |
||
563 | |||
564 | 35 | gtress | public synchronized void setCenter (int sx, int sy) { |
565 | 32 | gtress | if (sx == this.sx && sy == this.sy) return; |
566 | this.sx = sx;
|
||
567 | this.sy = sy;
|
||
568 | steps = 0;
|
||
569 | } |
||
570 | |||
571 | 35 | gtress | public synchronized void setRadius (int r, int dr) { |
572 | 32 | gtress | this.r = r;
|
573 | this.dr = dr;
|
||
574 | steps = 0;
|
||
575 | } |
||
576 | |||
577 | 35 | gtress | public void run () { |
578 | 32 | gtress | running = true;
|
579 | 35 | gtress | while (running) {
|
580 | 32 | gtress | step(); |
581 | 35 | gtress | try {
|
582 | Thread.sleep(INDICATOR_DELAY);
|
||
583 | } catch (InterruptedException e) { |
||
584 | running = false;
|
||
585 | return;
|
||
586 | } |
||
587 | 32 | gtress | } |
588 | } |
||
589 | |||
590 | 35 | gtress | private synchronized void step () { |
591 | 32 | gtress | Polygon poly1_new = new Polygon(); |
592 | Polygon poly2_new = new Polygon(); |
||
593 | Polygon poly3_new = new Polygon(); |
||
594 | Polygon poly4_new = new Polygon(); |
||
595 | |||
596 | //the step
|
||
597 | theta = (theta + DTHETA/Math.PI) % (Math.PI); |
||
598 | |||
599 | //the calculation
|
||
600 | //let p be the point of the pointy thing toward the center
|
||
601 | //let r be the point at the opposite side
|
||
602 | |||
603 | //recalculate radius, if it will look cool, lolz
|
||
604 | int newr = r;
|
||
605 | 38 | gtress | if (steps < 100) |
606 | newr = (int)( r + 200/(steps+1) ); |
||
607 | 32 | gtress | |
608 | //precompute values for dx and dy
|
||
609 | int dx_inner = (int)(newr * Math.cos(theta)); |
||
610 | int dy_inner = (int)(newr * Math.sin(theta)); |
||
611 | int dx_outer = (int)((newr+dr) * Math.cos(theta)); |
||
612 | int dy_outer = (int)((newr+dr) * Math.sin(theta)); |
||
613 | |||
614 | //calculate polygon constants
|
||
615 | int dy_poly = (int)(dr/2 * Math.cos(theta)); |
||
616 | int dx_poly = (int)(dr/2 * Math.sin(theta)); |
||
617 | |||
618 | //determine critical points
|
||
619 | 35 | gtress | //kansas city shuffle!
|
620 | 32 | gtress | px1 = sx + dx_inner; |
621 | py1 = sy - dy_inner; |
||
622 | rx1 = sx + dx_outer; |
||
623 | ry1 = sy - dy_outer; |
||
624 | px2 = sx - dx_inner; |
||
625 | py2 = sy + dy_inner; |
||
626 | rx2 = sx - dx_outer; |
||
627 | ry2 = sy + dy_outer; |
||
628 | px3 = sx - dy_inner; |
||
629 | py3 = sy - dx_inner; |
||
630 | rx3 = sx - dy_outer; |
||
631 | ry3 = sy - dx_outer; |
||
632 | px4 = sx + dy_inner; |
||
633 | py4 = sy + dx_inner; |
||
634 | rx4 = sx + dy_outer; |
||
635 | ry4 = sy + dx_outer; |
||
636 | |||
637 | //create polygons
|
||
638 | poly1_new.addPoint(px1, py1); |
||
639 | poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly); |
||
640 | poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly); |
||
641 | poly2_new.addPoint(px2, py2); |
||
642 | poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly); |
||
643 | poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly); |
||
644 | poly3_new.addPoint(px3, py3); |
||
645 | poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly); |
||
646 | poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly); |
||
647 | poly4_new.addPoint(px4, py4); |
||
648 | poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly); |
||
649 | poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly); |
||
650 | |||
651 | //reassign updated polygons
|
||
652 | poly1 = poly1_new; |
||
653 | poly2 = poly2_new; |
||
654 | poly3 = poly3_new; |
||
655 | poly4 = poly4_new; |
||
656 | |||
657 | if (steps < 300) steps++; |
||
658 | } |
||
659 | |||
660 | 35 | gtress | public synchronized void draw () { |
661 | 32 | gtress | if (!running) return; |
662 | g.setColor(Color.GRAY);
|
||
663 | //draw polygons
|
||
664 | g.fillPolygon(poly1); |
||
665 | g.fillPolygon(poly2); |
||
666 | g.fillPolygon(poly3); |
||
667 | g.fillPolygon(poly4); |
||
668 | } |
||
669 | |||
670 | } |
||
671 | |||
672 | /*
|
||
673 | * Simulator thread.
|
||
674 | *
|
||
675 | */
|
||
676 | 35 | gtress | private class Simulator extends Thread { |
677 | 32 | gtress | final int SIMULATOR_DELAY = 300; |
678 | boolean running;
|
||
679 | |||
680 | 35 | gtress | public Simulator () {
|
681 | 32 | gtress | super("Simulator"); |
682 | running = false;
|
||
683 | } |
||
684 | |||
685 | 35 | gtress | public void run () { |
686 | 32 | gtress | running = true;
|
687 | 35 | gtress | while (running) {
|
688 | 32 | gtress | step(); |
689 | 35 | gtress | try {
|
690 | Thread.sleep(SIMULATOR_DELAY);
|
||
691 | } catch (InterruptedException e) { |
||
692 | running = false;
|
||
693 | return;
|
||
694 | } |
||
695 | 32 | gtress | } |
696 | } |
||
697 | |||
698 | 35 | gtress | private void step () { |
699 | 32 | gtress | // simulate passing the token
|
700 | moveToken(); |
||
701 | } |
||
702 | |||
703 | } |
||
704 | |||
705 | /*
|
||
706 | * PacketMonitor thread.
|
||
707 | *
|
||
708 | * Currently, this counts the rate of token passes but will eventually
|
||
709 | * be modified to keep more important statistics.
|
||
710 | */
|
||
711 | 35 | gtress | private class PacketMonitor extends Thread { |
712 | 32 | gtress | final int PACKETMONITOR_DELAY = 1000; |
713 | |||
714 | boolean running;
|
||
715 | int tokenPasses;
|
||
716 | |||
717 | 35 | gtress | public PacketMonitor () {
|
718 | 32 | gtress | super("PacketMonitor"); |
719 | running = false;
|
||
720 | tokenPasses = 0;
|
||
721 | } |
||
722 | |||
723 | 35 | gtress | public void run () { |
724 | 32 | gtress | running = true;
|
725 | 35 | gtress | while (running) {
|
726 | 32 | gtress | displayTokenPasses(); |
727 | 35 | gtress | try {
|
728 | Thread.sleep(PACKETMONITOR_DELAY);
|
||
729 | } catch (InterruptedException e) { |
||
730 | running = false;
|
||
731 | return;
|
||
732 | } |
||
733 | 32 | gtress | } |
734 | } |
||
735 | |||
736 | 35 | gtress | public synchronized void addTokenPass () { |
737 | 32 | gtress | tokenPasses++; |
738 | } |
||
739 | |||
740 | 35 | gtress | public synchronized void displayTokenPasses () { |
741 | 32 | gtress | lblTokenPasses.setText("" + tokenPasses);
|
742 | tokenPasses = 0;
|
||
743 | } |
||
744 | |||
745 | } |
||
746 | |||
747 | /*
|
||
748 | * DataListener thread.
|
||
749 | *
|
||
750 | */
|
||
751 | 35 | gtress | class DataListener extends Thread { |
752 | 38 | gtress | final int DATALISTENER_DELAY = 666; |
753 | 32 | gtress | BufferedReader reader;
|
754 | |||
755 | 35 | gtress | public DataListener () {
|
756 | 32 | gtress | super("Colonet DataListener"); |
757 | } |
||
758 | |||
759 | 35 | gtress | public void run () { |
760 | 32 | gtress | String line;
|
761 | 35 | gtress | while (true) { |
762 | 32 | gtress | try {
|
763 | 38 | gtress | if (csi == null) return; |
764 | line = csi.getLine(); |
||
765 | if (line != null) { |
||
766 | csi.msg("Incoming data: [" + line + "]"); |
||
767 | //TODO: parse incoming data here
|
||
768 | } |
||
769 | 32 | gtress | Thread.sleep(DATALISTENER_DELAY);
|
770 | } catch (InterruptedException e) { |
||
771 | return;
|
||
772 | } catch (IOException e) { |
||
773 | 35 | gtress | csi.warn("IOException while reading incoming data.");
|
774 | 32 | gtress | } |
775 | } |
||
776 | } |
||
777 | |||
778 | } |
||
779 | |||
780 | } |