View Javadoc

1   /*
2    * @(#)$Id: WiiReaderPanel.java
3    *
4    * Author       :   Arthur Vogels, arthur.vogels@gmail.com
5    *
6    * Purpose      :	Draws the data recorded by the WiiReader into
7    * 					planes and graphs on a JPanel.
8    *
9    *
10   * -----------------------------------------------------------------------
11   *
12   * Revision Information:
13   *
14   * Date             Who         	Reason
15   *
16   * Dec 16, 2008     arthurvogels    Initial Release
17   *
18   * -----------------------------------------------------------------------
19   *
20   * Copyright 1999-2009 ETH Zurich. All Rights Reserved.
21   *
22   * This software is the proprietary information of ETH Zurich.
23   * Use is subject to license terms.
24   * 
25   */
26  
27  package org.ximtec.igesture.algorithm.rubine3d.tools;
28  
29  import java.awt.Color;
30  import java.awt.Cursor;
31  import java.awt.Font;
32  import java.awt.Graphics;
33  import java.awt.Point;
34  import java.awt.Rectangle;
35  import java.util.ArrayList;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Vector;
39  
40  import javax.swing.JPanel;
41  
42  import org.sigtec.ink.Note;
43  import org.sigtec.ink.Trace;
44  import org.ximtec.igesture.core.Gesture;
45  import org.ximtec.igesture.core.GestureSample;
46  import org.ximtec.igesture.core.GestureSample3D;
47  import org.ximtec.igesture.util.additions3d.AccelerationSample;
48  import org.ximtec.igesture.util.additions3d.Accelerations;
49  
50  public class WiiReaderPanel extends JPanel {
51  
52  	private WiiReader reader;
53  	private GestureSample3D gs;
54  
55  	/**
56  	 * Constructor
57  	 * 
58  	 * @param reader
59  	 *            The WiiReader this WiiReaderPanel belongs to
60  	 */
61  	public WiiReaderPanel(WiiReader reader) {
62  		this.reader = reader;
63  		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
64  	}	
65  	
66  	/**
67  	 * Override of the JPanel paintComponent method. Paints the gesture from
68  	 * reader in 3 planes onto the Graphics object
69  	 */
70  	@Override
71  	public void paintComponent(Graphics g) {
72  		//System.err.println("paintComponent: reader = " + reader);
73  		// Clear first
74  //		g.clearRect(0, 0, getWidth(), getHeight());
75  		g.setColor(Color.WHITE);
76  		g.fillRect(0, 0, getWidth(), getHeight());
77  		g.setColor(Color.BLACK);
78  		
79  		if (reader != null) {
80  			// Get gesture to draw
81  			gs = (GestureSample3D) reader.getGesture();
82  		}
83  		// Calculate the drawing fields for the planes
84  		List<Rectangle> fields = calculateFieldSizes(0.02);
85  		Rectangle XYfield = fields.get(0);
86  		Rectangle YZfield = fields.get(1);
87  		Rectangle ZXfield = fields.get(2);
88  		Rectangle graphField = fields.get(3);
89  		//Draw rectangles around the fields
90  		g.drawRect((int) XYfield.getX(), (int) XYfield.getY(), (int) XYfield.getWidth(), (int) XYfield.getHeight());
91  		g.drawRect((int) YZfield.getX(), (int) YZfield.getY(), (int) YZfield.getWidth(), (int) YZfield.getHeight());
92  		g.drawRect((int) ZXfield.getX(), (int) ZXfield.getY(), (int) ZXfield.getWidth(), (int) ZXfield.getHeight());
93  		g.drawRect((int) graphField.getX(), (int) graphField.getY(), (int) graphField.getWidth(), (int) graphField.getHeight());
94  		//Draw titles in the fields
95  		g.setColor(Color.RED);
96  		g.drawString("XY-Plane", (int) (XYfield.getX() + 10),(int) (XYfield.getY() + 15));
97  		g.drawString("YZ-Plane", (int) (YZfield.getX() + 10),(int) (YZfield.getY() + 15));
98  		g.drawString("ZX-Plane", (int) (ZXfield.getX() + 10),(int) (ZXfield.getY() + 15));
99  		g.drawString("Accelerations", (int) (graphField.getX() + 10),(int) (graphField.getY() + 15));
100 		
101 		
102 		if (gs != null) {
103 
104 			Accelerations acc = gs.getGesture().getAccelerations();
105 
106 			//System.err.println("Accelerations: " + gs.getGesture().getAccelerations());
107 			
108 			// Split the gesture to three planes of type Gesture<Note>
109 			List<Gesture<Note>> notes = gs.splitToPlanes();
110 
111 			// Get traces out of the note
112 			List<Trace> traces = new Vector<Trace>();
113 			for (int i = 0; i < notes.size(); i++) {
114 				traces.add(notes.get(i).getGesture().get(0));
115 			}
116 			traces = scaleTraces(traces, 10, fields.subList(0, 3));
117 			notes.clear();
118 			for (int i = 0; i < traces.size(); i++) {
119 				Note note = new Note();
120 				note.add(traces.get(i));
121 				Gesture<Note> gesture = new GestureSample(reader,"", note);
122 				notes.add(gesture);
123 			}
124 
125 			// Get the planes from the list for convenience
126 			Gesture<Note> gest = notes.get(0);
127 			Note noteXY = gest.getGesture();
128 			gest = notes.get(1);
129 			Note noteYZ = gest.getGesture();
130 			gest = notes.get(2);
131 			Note noteZX = gest.getGesture();
132 			// Rectangle accelerationsField = fields.get(3);
133 			// Draw planes on g
134 			drawPlane(noteXY, XYfield, g);
135 			drawPlane(noteYZ, YZfield, g);
136 			drawPlane(noteZX, ZXfield, g);
137 			// Draw acceleration graphs
138 			drawAccelerationsGraph(acc, graphField, 0.02, g);
139 		}
140 	}
141 
142 	/**
143 	 * Draws a graph with wiimote accelerations on g
144 	 * 
145 	 * @param acc
146 	 *            The WiiAccelerations object containing the data for the graphs
147 	 * @param field
148 	 *            The rectangle in which the graph should be drawn
149 	 * @param spacePercentage
150 	 *            The space marging percentage at the sides
151 	 * @param title
152 	 *            The title of the graph
153 	 * @param g
154 	 *            The Graphics object on which the graph should be drawn
155 	 */
156 	private void drawAccelerationsGraph(Accelerations acc, Rectangle field,
157 			double spacePercentage, Graphics g) {
158 		g.setColor(Color.BLACK);
159 
160 		if (acc != null) {
161 
162 			// Calculate margin in pixels from spacePercentage
163 			int margin = (int) (spacePercentage * field.getWidth());
164 
165 			// Define fields for X, Y and Z acceleration graphs
166 			Rectangle fieldX = new Rectangle((int) field.getX() + margin,
167 					(int) field.getY() + margin, (int) field.getWidth()
168 							- (2 * margin), (int) (field.getHeight() / 3)
169 							- (2 * margin));
170 			Rectangle fieldY = new Rectangle((int) field.getX() + margin,
171 					(int) (field.getY() + field.getHeight() / 3) + margin,
172 					(int) field.getWidth() - (2 * margin), (int) (field
173 							.getHeight() / 3)
174 							- (2 * margin));
175 			Rectangle fieldZ = new Rectangle((int) field.getX() + margin,
176 					(int) (field.getY() + field.getHeight() * 0.67) + margin,
177 					(int) field.getWidth() - (2 * margin), (int) (field
178 							.getHeight() / 3)
179 							- (2 * margin));
180 
181 			// Create buffers of points to draw for X, Y and Z acceleration
182 			List<List<Point>> buffers = scaleAccelerations(acc, fieldX);
183 
184 			// Draw the graphs
185 			drawGraph(buffers.get(0), fieldX, Color.GRAY, Color.RED, "X-Axis",
186 					g);
187 			drawGraph(buffers.get(1), fieldY, Color.GRAY, Color.GREEN,
188 					"Y-Axis", g);
189 			drawGraph(buffers.get(2), fieldZ, Color.GRAY, Color.BLUE, "Z-Axis",
190 					g);
191 
192 		} else {
193 //			Font originalFont = g.getFont();
194 //			Font font = new Font("Arial", Font.PLAIN, 10);
195 //			g.setFont(font);
196 //			g.drawString("No acceleration data available",
197 //					(int) (field.getX() + 5),
198 //					(int) ((field.getY() + (0.5 * field.getHeight()))));
199 //			g.setFont(originalFont);
200 		}
201 	}
202 
203 	/**
204 	 * Scales acceleration data from acc to fit into a rectangle of the size of
205 	 * fieldSize in order to be drawn. Returns a lists of 3 lists of points.
206 	 * 
207 	 * @param acc
208 	 *            the WiiAccelerations object containing the input data
209 	 * @param fieldSize
210 	 *            A rectangle from which the size is used to scale into
211 	 * @return A List of Lists of Points, cotaining the scaled data
212 	 */
213 	private List<List<Point>> scaleAccelerations(Accelerations acc,
214 			Rectangle fieldSize) {
215 		// Retrieve timestamp of first and last sample
216 		long timeFirst = acc.getFirstSampleTime();
217 		long timeLast = acc.getLastSampleTime();
218 		// Retrieve maximum absolute acceleration value
219 		double maxAbsAcc = acc.getMaxAbsoluteAccelerationValue();
220 		// Find out how high and wide a graph can be
221 		double graphWidth = fieldSize.getWidth();
222 		double graphHeight = fieldSize.getHeight();
223 		// Calculate the vertical scaling factor
224 		double verticalScalingFactor = (0.5 * graphHeight) / maxAbsAcc;
225 		// Calculate the horizontal scaling factor
226 		long gestureLength = timeLast - timeFirst;
227 		double horizontalScalingFactor = graphWidth / gestureLength;
228 		// Calculate the positions to draw
229 		List<Point> bufferX = new Vector<Point>();
230 		List<Point> bufferY = new Vector<Point>();
231 		List<Point> bufferZ = new Vector<Point>();
232 		Iterator<AccelerationSample> i = acc.getSamples().iterator();
233 		while (i.hasNext()) {
234 			AccelerationSample s = i.next();
235 			Point point = new Point();
236 			// X position
237 			double xPos = horizontalScalingFactor
238 					* (s.getTimeStamp() - timeFirst);
239 			double yPos = (graphHeight / 2)
240 					- (verticalScalingFactor * (s.getXAcceleration()));
241 			point.setLocation(xPos, yPos);
242 			bufferX.add(point);
243 			// Y position
244 			point = new Point();
245 			yPos = (graphHeight / 2)
246 					- (verticalScalingFactor * (s.getYAcceleration()));
247 			point.setLocation(xPos, yPos);
248 			bufferY.add(point);
249 			// Z position
250 			point = new Point();
251 			yPos = (graphHeight / 2)
252 					- (verticalScalingFactor * (s.getZAcceleration()));
253 			point.setLocation(xPos, yPos);
254 			bufferZ.add(point);
255 		}
256 		List<List<Point>> buffers = new Vector<List<Point>>();
257 		buffers.add(bufferX);
258 		buffers.add(bufferY);
259 		buffers.add(bufferZ);
260 		return buffers;
261 	}
262 
263 	/**
264 	 * Draws Note plane in Rectangle field on Graphics g
265 	 * 
266 	 * @param plane
267 	 *            the Note that should be drawn
268 	 * @param field
269 	 *            The rectangle in which the Note should be drawn
270 	 * @param title
271 	 *            The title of the plane
272 	 * @param g
273 	 *            The Graphics object onto which the Note should be drawn
274 	 */
275 	private void drawPlane(Note plane, Rectangle field, Graphics g) {
276 		if (plane != null) {
277 			g.setColor(Color.BLACK);
278 			// Draw all traces from plane on g
279 			for (Trace trace : plane.getTraces()) {
280 				drawTrace(trace, 10, field, g);
281 			}
282 		}
283 	}
284 
285 	/**
286 	 * Draws Trace trace on Graphics g in Rectangle field, while maintaining
287 	 * margin from the sides of the rectangle
288 	 * 
289 	 * @param trace
290 	 *            The Trace that should be drawn
291 	 * @param margin
292 	 *            The margin to the sides of the field in pixels
293 	 * @param field
294 	 *            The Rectangle into which the trace should be drawn
295 	 * @param g
296 	 *            The Graphics object onto which the Trace should be drawn
297 	 */
298 	private void drawTrace(Trace trace, int margin, Rectangle field, Graphics g) {
299 		// Scale trace to match the field size, taking margin at the sides into
300 		// account
301 		// Create a buffer containing all the points of the trace
302 		List<Point> buffer = new ArrayList<Point>();
303 		for (org.sigtec.ink.Point point : trace.getPoints()) {
304 			buffer.add(new Point((int) point.getX(), (int) point.getY()));
305 		}
306 		// Draw lines on g from point to point within the field
307 		for (int i = 0; i < buffer.size() - 1; i++) {
308 			g.drawLine((int) (buffer.get(i).getX() + field.getX()),
309 					(int) (buffer.get(i).getY() + field.getY()), (int) (buffer
310 							.get(i + 1).getX() + field.getX()), (int) (buffer
311 							.get(i + 1).getY() + field.getY()));
312 		}
313 		// Draw a small circle to mark the startpoint of the gesture
314 		if (buffer.size() > 0)
315 			g.fillOval((int) (buffer.get(0).getX() + +field.getX()),
316 					(int) (buffer.get(0).getY() + field.getY()), 5, 5);
317 	}
318 
319 	/**
320 	 * Scales Traces to fit into the given Rectangles
321 	 * 
322 	 * @param traces
323 	 *            List of traces that should be scaled
324 	 * @param margin
325 	 *            The margin to the sides of the fields in pixels
326 	 * @param fields
327 	 *            List of Rectangles into which the traces should be scaled
328 	 * @return A list of scaled traces
329 	 */
330 	private List<Trace> scaleTraces(List<Trace> traces, int margin,
331 			List<Rectangle> fields) {
332 		List<Trace> tracesNew = new Vector<Trace>();
333 		double scalingFactor = 0;
334 		// find the scaling factor to use for all traces
335 		double maxXUsed = 0;
336 		double maxYUsed = 0;
337 		double minXAvailable = 100000;
338 		double minYAvailable = 100000;
339 		for (int i = 0; i < traces.size(); i++) {
340 			// Take trace and corresponding rectangle from lists
341 			Trace trace = traces.get(i);
342 			Rectangle field = fields.get(i);
343 			// Find extremes
344 			double maxFoundX = 0;
345 			double minFoundX = 0;
346 			double maxFoundY = 0;
347 			double minFoundY = 0;
348 			for (int j = 0; j < trace.getPoints().size(); j++) {
349 				org.sigtec.ink.Point point = trace.getPoints().get(j);
350 				if (point.getX() > maxFoundX)
351 					maxFoundX = point.getX();
352 				if (point.getY() > maxFoundY)
353 					maxFoundY = point.getY();
354 				if (point.getX() < minFoundX)
355 					minFoundX = point.getX();
356 				if (point.getY() < minFoundY)
357 					minFoundY = point.getY();
358 			}
359 			double xSizeUsed = maxFoundX - minFoundX;
360 			double ySizeUsed = maxFoundY - minFoundY;
361 
362 			if (xSizeUsed > maxXUsed)
363 				maxXUsed = xSizeUsed;
364 			if (ySizeUsed > maxYUsed)
365 				maxYUsed = ySizeUsed;
366 
367 			// Define the playing field
368 			double minAvailableX = field.getX() + margin;
369 			double minAvailableY = field.getY() + margin;
370 			double maxAvailableX = (field.getWidth() + field.getX()) - margin;
371 			double maxAvailableY = (field.getHeight() + field.getY()) - margin;
372 			double xSizeAvailable = maxAvailableX - minAvailableX;
373 			double ySizeAvailable = maxAvailableY - minAvailableY;
374 
375 			if (xSizeAvailable < minXAvailable)
376 				minXAvailable = xSizeAvailable;
377 			if (ySizeAvailable < minYAvailable)
378 				minYAvailable = ySizeAvailable;
379 
380 		}
381 		// Calculate scaling factor
382 		scalingFactor = findScalingFactor(minXAvailable, maxXUsed,
383 				minYAvailable, maxYUsed);
384 		// Scale every Trace using the found scalingfactor
385 		for (int i = 0; i < traces.size(); i++) {
386 			// Take trace and corresponding rectangle from lists
387 			Trace trace = traces.get(i);
388 
389 			// Find extremes
390 			double maxFoundX = 0;
391 			double minFoundX = 0;
392 			double maxFoundY = 0;
393 			double minFoundY = 0;
394 			for (int j = 0; j < trace.getPoints().size(); j++) {
395 				org.sigtec.ink.Point point = trace.getPoints().get(j);
396 				if (point.getX() > maxFoundX)
397 					maxFoundX = point.getX();
398 				if (point.getY() > maxFoundY)
399 					maxFoundY = point.getY();
400 				if (point.getX() < minFoundX)
401 					minFoundX = point.getX();
402 				if (point.getY() < minFoundY)
403 					minFoundY = point.getY();
404 			}
405 
406 			// Move every point inside the playing field
407 			Trace traceNew = new Trace();
408 			for (int k = 0; k < trace.getPoints().size(); k++) {
409 				double xNew = scalingFactor
410 						* (trace.getPoints().get(k).getX() - minFoundX)
411 						+ margin;
412 				double yNew = scalingFactor
413 						* (trace.getPoints().get(k).getY() - minFoundY)
414 						+ margin;
415 				traceNew.add(new org.sigtec.ink.Point(xNew, yNew));
416 			}
417 			tracesNew.add(traceNew);
418 		}
419 		return tracesNew;
420 	}
421 
422 	/**
423 	 * Calculates scaling factor
424 	 * 
425 	 * @param xSizeAvailable
426 	 *            Available size in x direction in pixels
427 	 * @param xSizeUsed
428 	 *            Used size in x direction in source data
429 	 * @param ySizeAvailable
430 	 *            Available size in y direction in pixels
431 	 * @param ySizeUsed
432 	 *            Used size in y direction in source data
433 	 * @return The scaling factor
434 	 */
435 	private double findScalingFactor(double xSizeAvailable, double xSizeUsed,
436 			double ySizeAvailable, double ySizeUsed) {
437 		double scalingFactor = xSizeAvailable / xSizeUsed;
438 		if ((ySizeAvailable / ySizeUsed) < scalingFactor) // take the smallest
439 			// factor so it will
440 			// definately fit
441 			scalingFactor = ySizeAvailable / ySizeUsed;
442 		return scalingFactor;
443 	}
444 
445 	/**
446 	 * Calculates the sizes and positions of the four fields on this panel,
447 	 * using the size of the panel
448 	 * 
449 	 * @param spacePercentage
450 	 *            The margin percentage for the fields from the sides of the
451 	 *            panel
452 	 * @return A list of 4 Rectangles, the fields for this panel
453 	 */
454 	private List<Rectangle> calculateFieldSizes(double spacePercentage) {
455 		// Keep a distance from the sides of the panel
456 		int spacerHorizontal = (int) (spacePercentage * getWidth());
457 		int spacerVertical = (int) (spacePercentage * getHeight());
458 		// Calculate width and height, they are the same for all four fields
459 		int fieldWidth = (int) ((getWidth() - (4 * spacerHorizontal)) * 0.5);
460 		int fieldHeight = (int) ((getHeight() - (4 * spacerVertical)) * 0.5);
461 		// Create field rectangles
462 		Rectangle field1 = new Rectangle(spacerHorizontal, spacerVertical,
463 				fieldWidth, fieldHeight);
464 		Rectangle field2 = new Rectangle((spacerHorizontal * 3) + fieldWidth,
465 				spacerVertical, fieldWidth, fieldHeight);
466 		Rectangle field3 = new Rectangle(spacerHorizontal, (spacerVertical * 3)
467 				+ fieldHeight, fieldWidth, fieldHeight);
468 		Rectangle field4 = new Rectangle((spacerHorizontal * 3) + fieldWidth,
469 				(spacerVertical * 3) + fieldHeight, fieldWidth, fieldHeight);
470 		// Add fields to return variable
471 		List<Rectangle> fields = new Vector<Rectangle>();
472 		fields.add(field1);
473 		fields.add(field2);
474 		fields.add(field3);
475 		fields.add(field4);
476 		// Return
477 		return fields;
478 	}
479 
480 	/**
481 	 * Draws a graph in color from data into rectangle on g
482 	 * 
483 	 * @param data
484 	 *            The source data for the graph
485 	 * @param field
486 	 *            The Rectangle into which the graph should be drawn
487 	 * @param axisColor
488 	 *            The color of the axis
489 	 * @param dataColor
490 	 *            The color of the data series in the graph
491 	 * @param title
492 	 *            The title of the graph
493 	 * @param g
494 	 *            The Graphics object onto which the graph should be drawn
495 	 */
496 	private void drawGraph(List<Point> data, Rectangle field, Color axisColor,
497 			Color dataColor, String title, Graphics g) {
498 		//System.err.println("Field: " + field.getX() + "," + field.getY() + " dimensions " + field.getWidth() + "," + field.getHeight());
499 		// Save original color
500 		Color originalColor = g.getColor();
501 		// Draw axes
502 		g.setColor(axisColor);
503 		g.drawLine((int) field.getX(), (int) (field.getY() + (0.5 * field
504 				.getHeight())), (int) (field.getX() + field.getWidth()),
505 				(int) (field.getY() + (0.5 * field.getHeight())));
506 		g.drawLine((int) field.getX(), (int) field.getY(), (int) field.getX(),
507 				(int) (field.getY() + field.getHeight()));
508 		// g.drawRect((int)field.getX(), (int)field.getY(),
509 		// (int)field.getWidth(), (int)field.getHeight());
510 		// Set data color
511 		g.setColor(dataColor);
512 		// Draw title
513 		Font originalFont = g.getFont();
514 		Font font = new Font("Arial", Font.PLAIN, 10);
515 		g.setFont(font);
516 		g.drawString(title, (int) (field.getX() + (0.5 * field.getWidth())),
517 				(int) (field.getY() + 15));
518 		g.setFont(originalFont);
519 		// Draw data
520 		Point lastPoint = new Point();
521 		if (data.size() > 0)
522 			lastPoint.setLocation((int) data.get(0).getX(), (int) data.get(0)
523 					.getY()); // Startpoint
524 		Iterator<Point> it = data.iterator();
525 		while (it.hasNext()) {
526 			Point p = it.next();
527 			int oldX = (int) (field.getX() + lastPoint.getX());
528 			int oldY = (int) (field.getY() + lastPoint.getY());
529 			int newX = (int) (field.getX() + p.getX());
530 			int newY = (int) (field.getY() + p.getY());
531 			g.drawLine(oldX, oldY, newX, newY);
532 			lastPoint = p;
533 			//System.err.println("old: (" + oldX + "," + oldY + ") , new: " + newX + "," + newY + ")");
534 		}
535 		// Set color back to original
536 		g.setColor(originalColor);
537 	}
538 
539 	/**
540 	 * Returns the gesture drawn by this panel
541 	 * 
542 	 * @return The gesture drawn by this panel
543 	 */
544 	public GestureSample3D getGesture() {
545 		return gs;
546 	}
547 
548 	/**
549 	 * Sets the gesture to be drawn by this panel
550 	 * 
551 	 * @param gs
552 	 *            The gesture to be drawn by this panel
553 	 */
554 	public void setGesture(GestureSample3D gs) {
555 		//System.err.println("SAMPLE : " + gs.getName());
556 		this.gs = gs;
557 		if(gs != null){
558 			System.err.println("WiiReaderPanel.setGesture() Displaying: " + gs.getName());
559 		}
560 		else {
561 			System.err.println("WiiReaderPanel.setGesture() Displaying: null gesture");
562 		}
563 		this.paintComponent(this.getGraphics());
564 	}
565 	
566 	public void clear()
567 	{
568 		System.out.println("clearing");
569 		paintComponent(getGraphics());
570 	}
571 
572 }