View Javadoc

1   /*
2    * @(#)$Id: JNote.java 689 2009-07-22 00:10:27Z bsigner $
3    *
4    * Author		:	Ueli Kurmann, igesture@uelikurmann.ch
5    *
6    * Purpose		:   Visualises a sigtec note.
7    *
8    * -----------------------------------------------------------------------
9    *
10   * Revision Information:
11   *
12   * Date				Who			Reason
13   *
14   * Nov 15, 2006     ukurmann    Initial Release
15   * Mar 24, 2007     bsigner     Cleanup
16   *
17   * -----------------------------------------------------------------------
18   *
19   * Copyright 1999-2009 ETH Zurich. All Rights Reserved.
20   *
21   * This software is the proprietary information of ETH Zurich.
22   * Use is subject to license terms.
23   * 
24   */
25  
26  
27  package hacks;
28  
29  import java.awt.Color;
30  import java.awt.Graphics;
31  import java.awt.Graphics2D;
32  import java.awt.RenderingHints;
33  import java.awt.geom.Point2D;
34  import java.awt.image.BufferedImage;
35  import java.io.File;
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.logging.Level;
40  import java.util.logging.Logger;
41  
42  import javax.imageio.ImageIO;
43  import javax.swing.BorderFactory;
44  import javax.swing.ImageIcon;
45  import javax.swing.JLabel;
46  
47  import org.sigtec.graphix.Points;
48  import org.sigtec.ink.Note;
49  import org.sigtec.ink.Point;
50  import org.sigtec.ink.input.TimestampedInputEvent;
51  import org.sigtec.ink.input.TimestampedLocation;
52  import org.sigtec.input.InputHandler;
53  import org.sigtec.util.Constant;
54  
55  
56  /**
57   * Visualises a sigtec note.
58   * 
59   * @version 1.0 Nov 2006
60   * @author Ueli Kurmann, igesture@uelikurmann.ch
61   * @author Beat Signer, signer@inf.ethz.ch
62   */
63  public class JNote extends JLabel implements InputHandler {
64  
65     private static final Logger LOGGER = Logger.getLogger(JNote.class.getName());
66  
67     private BufferedImage bufferedImage;
68  
69     private Graphics graphic;
70  
71     private Point lastPoint;
72  
73     private double scale;
74  
75     private int[] space;
76  
77     private int[] translation;
78  
79     private int traceNumber;
80  
81     private int initialSpaceWidth;
82  
83     private int initialSpaceHeight;
84  
85     private List<Point> locationBuffer;
86  
87     private static final int MAX_TIME = 180;
88  
89     private static final int MARKER_RADIUS = 6;
90  
91     private static final int INITIAL_WIDHT = 50;
92  
93     private static final int INITIAL_HEIGHT = 50;
94  
95     private static final int STRETCH_FACTOR = 2;
96  
97     private boolean freeze;
98  
99     public static String PNG = "png";
100 
101    public static String JPEG = "jpeg";
102 
103 
104    /**
105     * Constructs a new JNote.
106     * 
107     * @param width width of the component
108     * @param height height of the component
109     */
110    public JNote(int width, int height) {
111       this(width, height, INITIAL_WIDHT, INITIAL_HEIGHT);
112    }
113 
114 
115    /**
116     * Constructs a new JNote.
117     * 
118     * @param width width of the component.
119     * @param height height of the component.
120     * @param spaceWidth the initial width of the space to be represented.
121     * @param spaceHeight the initial height of the space to be represented.
122     */
123    public JNote(int width, int height, int spaceWidth, int spaceHeight) {
124       super();
125       traceNumber = 0;
126       locationBuffer = new ArrayList<Point>();
127       freeze = false;
128       this.initialSpaceHeight = spaceHeight;
129       this.initialSpaceWidth = spaceWidth;
130       space = new int[] { spaceWidth, spaceHeight };
131       setSize(width, height);
132       bufferedImage = new BufferedImage(width, height,
133             BufferedImage.TYPE_INT_ARGB);
134       graphic = bufferedImage.getGraphics();
135       ((Graphics2D)graphic).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
136             RenderingHints.VALUE_ANTIALIAS_ON);
137       setIcon(new ImageIcon(bufferedImage));
138       graphic.setColor(Color.BLACK);
139       this.setBorder(BorderFactory.createLineBorder(Color.black, 2));
140 
141    }
142 
143 
144    public void handle(Object invoker, TimestampedInputEvent timestampedEvent) {
145       if (timestampedEvent instanceof TimestampedLocation) {
146          TimestampedLocation location = (TimestampedLocation)timestampedEvent;
147 
148          if (freeze) {
149             clear();
150             freeze = false;
151          }
152 
153          drawNextPoint(location.toPoint());
154       }
155    } // handle
156 
157 
158    /**
159     * Sets a complete note to be drawn.
160     * 
161     * @param note the note to be drawn.
162     */
163    public void setNote(Note note) {
164       clear();
165 
166       for (final Point point : note.getPoints()) {
167          drawNextPoint(point);
168       }
169 
170    } // setNote
171 
172 
173    /**
174     * Freeze the current drawing. The next stroke will trigger the deletion of
175     * all current points and will start over.
176     * 
177     */
178    public void freeze() {
179       freeze = true;
180    } // freeze
181 
182 
183    /**
184     * Exports the drawing as an image file.
185     * 
186     * @param file the file in which the image of the note has to be stored.
187     * @param format the format of the stored image.
188     */
189    public void exportImage(File file, String format) {
190       try {
191          ImageIO.write(bufferedImage, format, file);
192       }
193       catch (final IOException e) {
194          LOGGER.log(Level.SEVERE, Constant.EMPTY_STRING, e);
195       }
196 
197    } // exportImage
198 
199 
200    /**
201     * Exports the drawing as a PNG file.
202     * 
203     * @param file the file in which the image of the note has to be stored.
204     */
205    public void exportPNGImage(File file) {
206       exportImage(file, PNG);
207    } // exportPNGImage
208 
209 
210    /**
211     * Draws the next point.
212     */
213    public synchronized void drawNextPoint(Point currentPoint) {
214       if (!locationBuffer.contains(currentPoint)) {
215          locationBuffer.add(currentPoint);
216       }
217 
218       if (translation == null) {
219          updateMiddlePoint(currentPoint);
220       }
221       else if (!isInArea(translate(currentPoint))) {
222          space[0] *= STRETCH_FACTOR;
223          space[1] *= STRETCH_FACTOR;
224          updateMiddlePoint(computeCenterPoint());
225       }
226 
227       if (lastPoint != null && isNewTrace(lastPoint, currentPoint)) {
228          drawLine(translate(lastPoint), translate(currentPoint));
229       }
230       else {
231          drawPoint(translate(currentPoint), MARKER_RADIUS);
232          numberTrace(translate(currentPoint), traceNumber++);
233       }
234 
235       repaint();
236       lastPoint = currentPoint;
237    } // drawNextPoint
238 
239 
240    /**
241     * Update the middle point.
242     */
243    private synchronized void updateMiddlePoint(Point point) {
244       scale = Math.min((double)bufferedImage.getWidth() / space[0],
245             (double)bufferedImage.getHeight() / space[1]);
246       translation = new int[2];
247       translation[0] = (int)(bufferedImage.getWidth() / 2 - point.getX() * scale);
248       translation[1] = (int)(bufferedImage.getHeight() / 2 - point.getY()
249             * scale);
250       refresh();
251    } // updateMiddlePoint
252 
253 
254    /**
255     * Redraw the entire drawing.
256     * 
257     */
258    private synchronized void refresh() {
259       clearBufferedImage();
260       traceNumber = 0;
261       final Point tmp = lastPoint;
262       lastPoint = null;
263 
264       for (final Point ts : locationBuffer) {
265          drawNextPoint(ts);
266       }
267 
268       lastPoint = tmp;
269    } // refresh
270 
271 
272    /**
273     * Draws a line between two given points.
274     * 
275     * @param startPoint the start point.
276     * @param endPoint the end point.
277     */
278    private void drawLine(Point startPoint, Point endPoint) {
279       graphic.drawLine((int)startPoint.getX(), (int)startPoint.getY(),
280             (int)endPoint.getX(), (int)endPoint.getY());
281    } // drawLine
282 
283 
284    /**
285     * Draws a point marking the start of a stroke.
286     * 
287     * @param point the point hold location information.
288     * @param radius the radius of the point.
289     */
290    private void drawPoint(Point point, int radius) {
291       graphic.setColor(Color.RED);
292       graphic.fillOval((int)point.getX() - radius / 2, (int)point.getY()
293             - radius / 2, radius, radius);
294       graphic.setColor(Color.BLACK);
295    } // drawPoint
296 
297 
298    /**
299     * Numbers the traces.
300     */
301    private void numberTrace(Point point, int number) {
302       graphic.setColor(Color.RED);
303       graphic.drawString(String.valueOf(number), (int)point.getX() - 5,
304             (int)point.getY() - 5);
305       graphic.setColor(Color.BLACK);
306    } // numberTrace
307 
308 
309    /**
310     * Translates the point.
311     * @param point the original point.
312     * @return the transformed point.
313     */
314    private Point translate(Point point) {
315       final double x = (point.getX() * scale + translation[0]);
316       final double y = (point.getY() * scale + translation[1]);
317       return new Point((int)x, (int)y);
318    } // translate
319 
320 
321    /**
322     * Tests if the point lies within the drawing area.
323     * 
324     * @param point the point to be tested.
325     * @return true if the point lies within the drawing area.
326     */
327    private boolean isInArea(Point point) {
328       if (point.getX() > bufferedImage.getWidth()
329             || point.getY() > bufferedImage.getHeight() || point.getX() < 0
330             || point.getY() < 0) {
331          return false;
332       }
333 
334       return true;
335    } // isInArea
336 
337 
338    /**
339     * Computes the cetner point of a drawing.
340     * 
341     * @return the drawing's center point.
342     */
343    private Point computeCenterPoint() {
344       final List<Point2D> points = new ArrayList<Point2D>();
345 
346       for (final Point ts : locationBuffer) {
347          points.add(new Point2D.Double(ts.getX(), ts.getY()));
348       }
349 
350       final Point2D point = Points.getCentre(points);
351       return new Point((int)point.getX(), (int)point.getY());
352    } // computeCenterPoint
353 
354 
355    /**
356     * Tests if a new strace was been started.
357     */
358    private boolean isNewTrace(Point loc1, Point loc2) {
359       return loc2.getTimestamp() - loc1.getTimestamp() < MAX_TIME;
360    } // isNewTrace
361 
362 
363    /**
364     * Clears the drawing area.
365     * 
366     */
367    public void clear() {
368       locationBuffer.clear();
369       lastPoint = null;
370       translation = null;
371       space = new int[] { initialSpaceWidth, initialSpaceHeight };
372       clearBufferedImage();
373       traceNumber = 0;
374       repaint();
375    } // clear
376 
377 
378    private void clearBufferedImage() {
379       graphic.setColor(Color.WHITE);
380       graphic
381             .fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
382       graphic.setColor(Color.BLACK);
383    } // clearBufferedImage
384 
385 
386    public BufferedImage getImage() {
387       return bufferedImage;
388    } // getImage
389 
390 }