Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / client / VectorController.java @ 638

History | View | Annotate | Download (7.51 KB)

1
import java.awt.*;
2
import java.awt.event.*;
3
import javax.swing.*;
4

    
5
/**
6
* Controls robot motion graphically, using a mouse-controlled adjustable "vector." 
7
* The controller uses the input vector to calculate a resulting velocity for each
8
* wheel on the robot. These velocities are also shown graphically. The vecolities
9
* are "normalized" internally to approximately eliminate the "dead zone" in which
10
* the robot applies partial power to the motors but does not move.
11
* @author Gregory Tress
12
*/
13
public class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
14
        // State variables
15
        int x, y, cx, cy;
16
        final Colonet colonet;
17
        
18
        // Painting constants
19
        final int WIDTH, HEIGHT;
20
        final int SIDE;
21
        final int BOT_SIZE = 70;
22
        final int WHEEL_SIZE = 15;
23

    
24
        public VectorController (Image img, Colonet colonet) {
25
                super (img);
26
                WIDTH = img.getWidth(null);
27
                HEIGHT = img.getHeight(null);
28
                cx = WIDTH/2;
29
                cy = HEIGHT/2;
30
                x = cx;
31
                y = cy;
32
                if (WIDTH < HEIGHT) {
33
                        SIDE = WIDTH;
34
                } else {
35
                        SIDE = HEIGHT;
36
                }
37
                this.colonet = colonet;
38
                
39
                this.addMouseListener(this);
40
                this.addMouseMotionListener(this);
41
        }
42

    
43
        /**  Set the robot motion vector. The "vector" is defined as 
44
        * (0,0)->(x,y) where (0,0) is in the center of the targeting ring.
45
        * @param x The x coordinate of the point.
46
        * @param y The y coordinate of the point.
47
        */
48
        public void setPoint (int x, int y) {
49
                if (isValidPoint(x, y)) {
50
                        this.x = x;
51
                        this.y = y;
52
                }
53
        }
54

    
55
        /**  Determines whether a point is inside the targeting ring.
56
        * @param x The x coordinate of the point.
57
        * @param y The y coordinate of the point.
58
        * @return True if a point is within the targeting ring.
59
        */
60
        public boolean isValidPoint (int x, int y) {
61
                double xsq = Math.pow(1.0*(x - cx)/(SIDE/2), 2);
62
                double ysq = Math.pow(1.0*(y - cy)/(SIDE/2), 2);
63
                return (xsq + ysq <= 1);
64
        }
65

    
66
        /**  Notifies the controller that a MouseEvent has occurred
67
        * on the controller surface. This should be called when a 
68
        * MouseListener attached to the controller has detected 
69
        * that a MouseEvent has occurred that may influence the state
70
        * of the controller, such as mouseClicked, mouseDragged, or
71
        * mouseReleased events.
72
        *
73
        * @param e The MouseEvent object which contains the details of the event.
74
        * @param send Determines whether the motion vector established
75
        *    as a result of the MouseEvent should subsequently
76
        *    be sent to the robot(s).
77
        */
78
        public void notifyMouseEvent (MouseEvent e, boolean send) {
79
                if (!isValidPoint(e.getX(), e.getY())) {
80
                        return;
81
                }
82
                setPoint(e.getX(), e.getY());
83
                repaint();
84
                if (send) {
85
                        Runnable r = new Runnable () {
86
                                public void run () {
87
                                        sendToServer();
88
                                }
89
                        };
90
                        (new Thread(r)).start();
91
                }
92
        }
93

    
94
        public void mouseExited(MouseEvent e) {
95
        }
96
        public void mouseEntered(MouseEvent e) {
97
        }
98
        public void mouseReleased(MouseEvent e) {
99
                notifyMouseEvent(e, true);
100
        }
101
        public void mouseClicked(MouseEvent e) {
102
                notifyMouseEvent(e, false);
103
        }
104
        public void mousePressed(MouseEvent e) {
105
        }
106
        public void mouseDragged(MouseEvent e) {
107
                notifyMouseEvent(e, false);
108
        }
109
        public void mouseMoved(MouseEvent e) {
110
        }
111

    
112
        /**  Calculates the magnitude of the vector. 
113
        * @return An int that is the truncated value of the speed of the vector.
114
        */
115
        public int getSpeed () {
116
                int dx = x - cx;
117
                int dy = y - cy;
118
                int s = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
119
                int maxspeed = SIDE/2;
120
                return s * 512 / SIDE;
121
        }
122

    
123
        /**
124
        * Returns the angle of the control vector in positive degrees west of north,
125
        * or negative degrees east of north, whichever is less than or equal to
126
        * 180 degrees total.
127
        */
128
        public int getAngle () {
129
                int dx = x - cx;
130
                int dy = cy - y;
131
                // find reference angle in radians
132
                double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
133
                // transform to degrees
134
                theta = theta * 180 / Math.PI;
135
                // adjust for quadrant
136
                if (dx < 0 && dy < 0)
137
                        theta = 90 + theta;
138
                else if (dx < 0 && dy >= 0)
139
                        theta = 90 - theta;
140
                else if (dx >= 0 && dy < 0)
141
                        theta = -90 - theta;
142
                else
143
                        theta = -90 + theta;
144
                return (int) theta;
145
        }
146
        
147
        private int getMotorL () {
148
                if (getSpeed() == 0)
149
                        return 0;
150
                int dx = x - cx;
151
                int dy = (cy - y) * 255 / getSpeed();
152
                int val = 0;
153
                // Dependent on quadrant
154
                if (dx < 0 && dy < 0)
155
                        val = -255;
156
                else if (dx < 0 && dy >= 0)
157
                        val = dy * 1024 / SIDE - 255;
158
                else if (dx >= 0 && dy < 0)
159
                        val = dy * 1024 / SIDE + 255;
160
                else
161
                        val = 255;
162
                // Normalize to 0-255
163
                return val * getSpeed() / 255;
164
        }
165
        
166
        private int getMotorR () {
167
                if (getSpeed() == 0)
168
                        return 0;
169
                int dx = x - cx;
170
                int dy = (cy - y) * 255 / getSpeed();
171
                int val = 0;
172
                // Dependent on quadrant
173
                if (dx < 0 && dy < 0)
174
                        val = dy * 1024 / SIDE + 255;
175
                else if (dx < 0 && dy >= 0)
176
                        val = 255;
177
                else if (dx >= 0 && dy < 0)
178
                        val = -255;
179
                else
180
                        val = dy * 1024 / SIDE - 255;
181
                // Normalize to 0-255
182
                return val * getSpeed() / 255;
183
        }
184
        
185
        private int eliminateDeadZone (int x) {
186
                final int START = 150;
187
                int val;
188
                if (x == 0)
189
                        return 0;
190
                if (x > 0)
191
                        val = (int) ((1 - 1.0 * START / 255) * x + START);
192
                else
193
                        val = (int) ((1 - 1.0 * START / 255) * x - START);
194
                return val;
195
        
196
        }
197

    
198
        public void paint (Graphics g) {
199
                // Clear image
200
                g.setColor(Color.BLACK);
201
                g.fillRect(0, 0, WIDTH, HEIGHT);
202
                ((Graphics2D)g).setStroke(new BasicStroke(1));
203
                
204
                // Motor indicators
205
                int motor1 = getMotorL() * BOT_SIZE / 512;
206
                int motor2 = getMotorR() * BOT_SIZE / 512;
207
                g.setColor(Color.YELLOW);
208
                if (motor1 < 0)
209
                        g.fillRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy, WHEEL_SIZE, -motor1);
210
                else
211
                        g.fillRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy-motor1, WHEEL_SIZE, motor1);
212
                if (motor2 < 0)
213
                        g.fillRect(cx+BOT_SIZE/2, cy, WHEEL_SIZE, -motor2);
214
                else
215
                        g.fillRect(cx+BOT_SIZE/2, cy-motor2, WHEEL_SIZE, motor2);
216
                
217
                // Watermark
218
                g.setColor(Color.GRAY);
219
                g.drawOval(cx-BOT_SIZE/2, cy-BOT_SIZE/2, BOT_SIZE, BOT_SIZE);
220
                g.drawRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy-BOT_SIZE/2, WHEEL_SIZE, BOT_SIZE);
221
                g.drawRect(cx+BOT_SIZE/2, cy-BOT_SIZE/2, WHEEL_SIZE, BOT_SIZE);
222
                
223
                // Targeting circle
224
                g.setColor(Color.RED);
225
                g.drawOval(cx-SIDE/2, cy-SIDE/2, SIDE, SIDE);
226
                ((Graphics2D)g).setStroke(new BasicStroke(2));
227
                
228
                // Vector Line
229
                g.setColor(Color.GREEN);
230
                g.drawLine(cx, cy, x, y);
231
                g.fillOval(x-3, y-3, 6, 6);
232
        }
233

    
234
        /** Set the controller to the maximum forward velocity. */
235
        public void setMaxForward () {
236
                setPoint(cx, cy - (SIDE/2) + 1);
237
        }
238

    
239
        /** Set the controller to the maximum reverse velocity. */
240
        public void setMaxReverse () {
241
                setPoint(cx, cy + (SIDE/2) - 1);
242
        }
243

    
244
        /** Set the controller to the maximum left velocity, 
245
        * causing to robot to turn counter-clockwise.
246
        */
247
        public void setMaxLeft () {
248
                setPoint(cx - (SIDE/2) + 1, cy);
249
        }
250

    
251
        /** Set the controller to the maximum right velocity, 
252
        * causing to robot to turn clockwise.
253
        */
254
        public void setMaxRight () {
255
                setPoint(cx + (SIDE/2) - 1, cy);
256
        }
257

    
258
        /** Set the controller to (0,0), the stopped state. */
259
        public void setZero () {
260
                setPoint(cx, cy);
261
        }
262

    
263
        /** Sends the current vector to the robot(s). */
264
        public void sendToServer () {
265
                // Determine destination ID
266
                String dest = ColonetServerInterface.GLOBAL_DEST;
267
                ColonetServerInterface csi = colonet.getCSI();
268
                /*if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
269
                        dest = (String)cmbRobotNum.getSelectedItem();
270
                }*/
271

    
272
                if (csi == null)
273
                        return;
274
                int motor1 = eliminateDeadZone(getMotorL());
275
                int motor2 = eliminateDeadZone(getMotorR());
276
                String motor1_string = (motor1 > 0) ? " 1 " + motor1 : " 0 " + (-motor1);
277
                String motor2_string = (motor2 > 0) ? " 1 " + motor2 : " 0 " + (-motor2);
278
                
279
                csi.sendData(ColonetServerInterface.MOVE + motor1_string + motor2_string, dest);
280
        }
281

    
282
}
283