InkPCanvas.java

00001 package edu.stanford.hci.r3.components;
00002 
00003 import java.awt.BasicStroke;
00004 import java.awt.Color;
00005 import java.awt.Dimension;
00006 import java.awt.geom.AffineTransform;
00007 import java.util.LinkedList;
00008 import java.util.List;
00009 
00010 import edu.stanford.hci.r3.pen.ink.Ink;
00011 import edu.stanford.hci.r3.pen.ink.InkStroke;
00012 import edu.stanford.hci.r3.util.geometry.CatmullRomSpline;
00013 import edu.umd.cs.piccolo.PCanvas;
00014 import edu.umd.cs.piccolo.PNode;
00015 import edu.umd.cs.piccolo.nodes.PPath;
00016 
00028 public class InkPCanvas extends PCanvas {
00029 
00030         public static final Color DARK_THEMED_INK_COLOR = new Color(0.85f, 0.85f, .95f, 0.8f);
00031 
00032         public static final Color DEFAULT_INK_COLOR = new Color(0.1f, 0.1f, .1f, 0.9f);
00033 
00037         private static final BasicStroke DEFAULT_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_ROUND,
00038                         BasicStroke.JOIN_ROUND);
00039 
00040         private Color defaultInkColor;
00041 
00045         private LinkedList<Ink> inkWell = new LinkedList<Ink>();
00046 
00047         private double pageOffsetX = 0;
00048 
00049         private double pageOffsetY = 0;
00050 
00051         private PNode strokesContainer;
00052 
00056         public InkPCanvas() {
00057                 setPreferredSize(new Dimension(320, 240));
00058                 setMinimumSize(new Dimension(320, 240));
00059                 strokesContainer = new PNode();
00060                 getLayer().addChild(strokesContainer);
00061                 useDefaultTheme();
00062         }
00063 
00069         public void addInk(Ink ink) {
00070                 inkWell.add(ink);
00071                 addInkPaths(ink.getStrokes(), ink.getColor());
00072                 getLayer().repaint();
00073         }
00074 
00080         @SuppressWarnings("unused")
00081         private void addInkBezierPaths(List<InkStroke> strokes, Color inkColor) {
00082 
00083                 // Each Stroke will be One PPath (it's just more efficient this way)
00084                 for (final InkStroke s : strokes) {
00085 
00086                         final double[] xArr = s.getXSamples();
00087                         final double[] yArr = s.getYSamples();
00088 
00089                         final PPath strokePath = new PPath();
00090                         // ink stroke style
00091                         strokePath.setStroke(DEFAULT_STROKE);
00092                         // the ink color
00093                         strokePath.setStrokePaint(inkColor);
00094                         strokePath.addAttribute("timestamp", new Long(s.getFirstTimestamp()));
00095                         strokesContainer.addChild(strokePath);
00096 
00097                         final int len = xArr.length;
00098                         if (len > 0) {
00099                                 strokePath.moveTo((float) (xArr[0] + pageOffsetX), (float) (yArr[0] + pageOffsetY));
00100                         }
00101 
00102                         // keeps last known "good point"
00103                         double lastGoodX = xArr[0];
00104                         double lastGoodY = yArr[0];
00105 
00106                         // connect the samples w/ quadratic curve segments
00107                         int numPointsCollected = 0;
00108                         for (int i = 0; i < len; i++) {
00109                                 final double currX = xArr[i];
00110                                 final double currY = yArr[i];
00111 
00112                                 numPointsCollected++;
00113 
00114                                 final double diffFromLastX = currX - lastGoodX;
00115                                 final double diffFromLastY = currY - lastGoodY;
00116 
00117                                 if (Math.abs(diffFromLastX) > 500 || Math.abs(diffFromLastY) > 500) {
00118                                         // too much error; eliminate totally random data...
00119                                         strokePath.lineTo((float) (lastGoodX + pageOffsetX), (float) (lastGoodY + pageOffsetY));
00120                                 } else {
00121 
00122                                         if (numPointsCollected == 2) {
00123                                                 numPointsCollected = 0;
00124 
00125                                                 // OK, not that much error
00126                                                 strokePath.quadTo((float) (lastGoodX + pageOffsetX),
00127                                                                 (float) (lastGoodY + pageOffsetY), (float) (currX + pageOffsetX),
00128                                                                 (float) (currY + pageOffsetY));
00129                                         }
00130 
00131                                         // set the last known good point
00132                                         lastGoodX = currX;
00133                                         lastGoodY = currY;
00134                                 }
00135                         }
00136 
00137                         // if there's any points left, just render them
00138                         if (numPointsCollected == 1) {
00139                                 strokePath.lineTo((float) (lastGoodX + pageOffsetX), (float) (lastGoodY + pageOffsetY));
00140                         }
00141 
00142                 }
00143 
00144                 strokesContainer.repaint();
00145         }
00146 
00152         private void addInkPaths(List<InkStroke> strokes, Color inkColor) {
00153                 // Each Stroke will be One PPath (it's just more efficient this way)
00154                 for (final InkStroke s : strokes) {
00155                         final CatmullRomSpline crspline = new CatmullRomSpline();
00156                         final double[] x = s.getXSamples();
00157                         final double[] y = s.getYSamples();
00158                         crspline.setPoints(x, y);
00159 
00160                         final PPath strokePath = new PPath(crspline.getShape());
00161 
00162                         // ink stroke style
00163                         strokePath.setStroke(DEFAULT_STROKE);
00164 
00165                         // the ink color
00166                         strokePath.setStrokePaint(inkColor);
00167                         strokePath.addAttribute("timestamp", new Long(s.getFirstTimestamp()));
00168                         strokesContainer.addChild(strokePath);
00169                 }
00170                 strokesContainer.repaint();
00171         }
00172 
00178         public void addInkWithDefaultColor(Ink ink) {
00179                 inkWell.add(ink);
00180                 addInkPaths(ink.getStrokes(), defaultInkColor);
00181                 getLayer().repaint();
00182         }
00183 
00187         public void resetViewOffsetAndScale() {
00188                 getCamera().setViewTransform(new AffineTransform());
00189         }
00190 
00194         public void setInk(Ink ink) {
00195                 inkWell.clear();
00196                 inkWell.add(ink);
00197                 strokesContainer.removeAllChildren();
00198                 addInkPaths(ink.getStrokes(), ink.getColor());
00199                 getLayer().repaint();
00200         }
00201 
00205         public void setStrokesScale(double scaleFactor) {
00206                 strokesContainer.setScale(scaleFactor);
00207         }
00208 
00212         public void useDarkTheme() {
00213                 setBackground(new Color(40, 40, 40));
00214                 defaultInkColor = DARK_THEMED_INK_COLOR;
00215         }
00216 
00217         public void useDefaultTheme() {
00218                 setBackground(new Color(240, 240, 240));
00219                 defaultInkColor = DEFAULT_INK_COLOR;
00220         }
00221 }

Generated on Sat Apr 14 18:21:36 2007 for R3 Paper Toolkit by  doxygen 1.4.7