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