View Javadoc
1 /****************************************************************************** 2 * Copyright (C) 2003 Jean-Daniel Fekete and INRIA, France * 3 * ------------------------------------------------------------------------- * 4 * This software is published under the terms of the X11 Software License * 5 * a copy of which has been included with this distribution in the * 6 * license-infovis.txt file. * 7 *****************************************************************************/ 8 package edu.psu.geovista.ui; 9 10 import java.awt.*; 11 import java.awt.event.*; 12 import java.awt.geom.*; 13 import java.util.*; 14 15 import javax.swing.*; 16 import javax.swing.Timer; 17 18 19 20 /*** 21 * Display excentric labels around items in a visualization. 22 * 23 * @author Jean-Daniel Fekete 24 * @version $Revision: 1.2 $ 25 */ 26 public class ExcentricLabels extends MouseAdapter implements Comparator, 27 MouseMotionListener { 28 Timer insideTimer; 29 int[] hits;//changed from IntColumn to int[] by fah 30 Rectangle2D.Double cursorBounds; 31 int centerX; 32 int centerY; 33 int focusSize = 50; 34 Point2D.Double[] itemPosition; 35 Point2D.Double[] linkPosition; 36 Point2D.Double[] labelPosition; 37 Point2D.Double[] left; 38 int leftCount; 39 Point2D.Double[] right; 40 int rightCount; 41 boolean xStable; 42 boolean yStable; 43 JComponent vpanel; 44 ExcentricLabelClient client; 45 boolean visible; 46 int gap = 5; 47 int maxLabels; 48 int labelCount; 49 int threshold = 20; 50 boolean opaque; 51 Color backgroundColor = Color.WHITE; 52 53 /*** 54 * Constructor for ExcentricLabels. 55 */ 56 public ExcentricLabels() { 57 cursorBounds = new Rectangle2D.Double(0, 0, focusSize, focusSize); 58 59 insideTimer = new Timer(2000, 60 new ActionListener() { 61 public void actionPerformed(ActionEvent e) { 62 setVisible(true); 63 } 64 }); 65 insideTimer.setRepeats(false); 66 setMaxLabels(20); 67 } 68 69 /*** 70 * @see javax.swing.JToolTip#setComponent(JComponent) 71 */ 72 public void setComponent(JComponent c) { 73 if (c instanceof ExcentricLabelClient) { 74 if (vpanel == c) 75 return; 76 if (vpanel != null) { 77 vpanel.removeMouseListener(this); 78 vpanel.removeMouseMotionListener(this); 79 //visualization = null; 80 client = null; 81 82 } 83 vpanel = c; 84 if (vpanel != null) { 85 //visualization = vpanel.getVisualization(); 86 client = (ExcentricLabelClient)c; 87 vpanel.addMouseListener(this); 88 vpanel.addMouseMotionListener(this); 89 } 90 } else 91 throw new RuntimeException("Invalid component type for ExcentricLabels"); 92 } 93 94 public void paint(Graphics2D graphics, Rectangle2D bounds) { 95 //if (visualization == null || !visible) 96 if (client == null || !visible) { // added fah 18 march 2003 97 return; 98 } 99 FontMetrics fm = graphics.getFontMetrics(); 100 computeExcentricLabels(graphics, bounds); 101 102 Line2D.Double line = new Line2D.Double(); 103 for (int i = 0; i < labelCount; i++) { 104 int hit = hits[i]; 105 String lab = client.getObservationLabel(hit);// added fah 18 march 2003 106 //visualization.getLabel(hits.get(i)); 107 if (lab == null) { 108 lab = "item" + hits[i]; 109 } 110 111 Point2D.Double pos = labelPosition[i]; 112 if (opaque) { 113 graphics.setColor(backgroundColor); 114 Rectangle2D sb = fm.getStringBounds(lab, graphics); 115 graphics.fillRect((int)(pos.x+sb.getX()-2), (int)(pos.y+sb.getY()-2), 116 (int)sb.getWidth()+4, (int)sb.getHeight()+4); 117 } 118 graphics.setColor(Color.BLACK); 119 graphics.drawString(lab, (int)(pos.x), (int)(pos.y)); 120 line.setLine(itemPosition[i], linkPosition[i]); 121 graphics.setXORMode(Color.white); 122 graphics.draw(line); 123 graphics.setPaintMode(); 124 } 125 graphics.setColor(Color.RED); 126 graphics.draw(cursorBounds); 127 } 128 129 protected void computeExcentricLabels(Graphics2D graphics, 130 Rectangle2D bounds) { 131 if (client == null) 132 return; 133 134 cursorBounds.x = centerX - focusSize / 2; 135 cursorBounds.y = centerY - focusSize / 2; 136 137 //if (hits == null) hits = new IntColumn("pickAll"); 138 139 hits = client.pickAll(cursorBounds); 140 //hits = visualization.pickAll(cursorBounds, bounds, hits); 141 142 labelCount = Math.min(maxLabels, hits.length); 143 if (labelCount != 0) { 144 computeItemPositions(graphics, bounds); 145 projectLeftRight(graphics, bounds); 146 } 147 } 148 149 protected void computeItemPositions(Graphics2D graphics, 150 Rectangle2D bounds) { 151 Rectangle2D.Double inter = new Rectangle2D.Double(); 152 153 for (int i = 0; i < labelCount; i++) { 154 //Rectangle2D rect = visualization.getShapeAt(hits.get(i)).getBounds2D(); 155 int hit = hits[i]; 156 Rectangle2D rect = client.getShapeAt(hit).getBounds2D(); 157 rect.intersect(cursorBounds, rect, inter); 158 itemPosition[i].setLocation(inter.getCenterX(), inter.getCenterY()); 159 } 160 } 161 162 protected double comparableValueLeft(Point2D.Double pos) { 163 if (yStable) 164 return pos.y; 165 else 166 return Math.atan2(pos.y - centerY, centerX - pos.x); 167 } 168 169 protected double comparableValueRight(Point2D.Double pos) { 170 if (yStable) 171 return pos.getY(); 172 else 173 return Math.atan2(pos.y - centerY, pos.x - centerX); 174 } 175 176 protected void projectLeftRight(Graphics2D graphics, Rectangle2D bounds) { 177 int radius = focusSize / 2; 178 int i; 179 180 leftCount = 0; 181 rightCount = 0; 182 double maxHeight = 0; 183 FontMetrics fm = graphics.getFontMetrics(); 184 185 for (i = 0; i < labelCount; i++) { 186 Point2D.Double itemPos = itemPosition[i]; 187 int hit = hits[i]; 188 String lab = client.getObservationLabel(hit); 189 if (lab == null) 190 lab = "item" + hits[i]; 191 Rectangle2D sb = fm.getStringBounds(lab, graphics); 192 Point2D.Double linkPos = linkPosition[i]; 193 Point2D.Double labelPos = labelPosition[i]; 194 195 maxHeight = Math.max(sb.getHeight(), maxHeight); 196 if (itemPosition[i].getX() < centerX) { 197 linkPos.y = comparableValueLeft(itemPos); 198 if (xStable) 199 linkPos.x = itemPos.x - radius - gap; 200 else 201 linkPos.x = centerX - radius - gap; 202 labelPos.x = linkPos.x - sb.getWidth(); 203 left[leftCount++] = linkPos; 204 } else { 205 linkPos.y = comparableValueRight(itemPos); 206 if (xStable) 207 linkPos.x = itemPos.x + radius + gap; 208 else 209 linkPos.x = centerX + radius + gap; 210 labelPos.x = linkPos.x; 211 right[rightCount++] = linkPos; 212 } 213 } 214 215 Arrays.sort(left, 0, leftCount, this); 216 Arrays.sort(right, 0, rightCount, this); 217 double yMidLeft = leftCount * maxHeight / 2; 218 double yMidRight = rightCount * maxHeight / 2; 219 int ascent = fm.getAscent(); 220 221 for (i = 0; i < leftCount; i++) { 222 Point2D.Double pos = left[i]; 223 pos.y = i * maxHeight + centerY - yMidLeft + ascent; 224 } 225 for (i = 0; i < rightCount; i++) { 226 Point2D.Double pos = right[i]; 227 pos.y = i * maxHeight + centerY - yMidRight + ascent; 228 } 229 for (i = 0; i < linkPosition.length; i++) { 230 labelPosition[i].y = linkPosition[i].y; 231 } 232 } 233 234 /*** 235 * Returns the visible. 236 * @return boolean 237 */ 238 public boolean isVisible() { 239 return visible; 240 } 241 242 /*** 243 * Sets the visible. 244 * @param visible The visible to set 245 */ 246 public void setVisible(boolean visible) { 247 if (this.visible != visible) { 248 this.visible = visible; 249 client.repaint(); 250 //visualization.repaint(); 251 } 252 } 253 254 /*** 255 * For sorting points vertically. 256 * 257 * @see java.util.Comparator#compare(Object, Object) 258 */ 259 public int compare(Object o1, Object o2) { 260 double d = ((Point2D.Double)o1).getY() - ((Point2D.Double)o2).getY(); 261 if (d < 0) 262 return -1; 263 else if (d == 0) 264 return 0; 265 else 266 return 1; 267 } 268 269 /*** 270 * @see java.awt.event.MouseAdapter#mouseEntered(MouseEvent) 271 */ 272 public void mouseEntered(MouseEvent e) { 273 insideTimer.restart(); 274 } 275 276 /*** 277 * @see java.awt.event.MouseAdapter#mouseExited(MouseEvent) 278 */ 279 public void mouseExited(MouseEvent e) { 280 insideTimer.stop(); 281 setVisible(false); 282 } 283 284 /*** 285 * @see java.awt.event.MouseAdapter#mousePressed(MouseEvent) 286 */ 287 public void mousePressed(MouseEvent e) { 288 setVisible(false); 289 } 290 291 /*** 292 * @see java.awt.event.MouseMotionListener#mouseDragged(MouseEvent) 293 */ 294 public void mouseDragged(MouseEvent e) { 295 } 296 297 int dist2(int dx, int dy) { 298 return dx * dx + dy * dy; 299 } 300 301 /*** 302 * @see java.awt.event.MouseMotionListener#mouseMoved(MouseEvent) 303 */ 304 public void mouseMoved(MouseEvent e) { 305 if (isVisible()) { 306 if (dist2(centerX - e.getX(), centerY - e.getY()) > threshold * threshold) { 307 setVisible(false); 308 insideTimer.restart(); 309 } 310 client.repaint(); 311 //visualization.repaint(); 312 } 313 centerX = e.getX(); 314 centerY = e.getY(); 315 } 316 317 /*** 318 * Returns the gap. 319 * @return int 320 */ 321 public int getGap() { 322 return gap; 323 } 324 325 /*** 326 * Sets the gap. 327 * @param gap The gap to set 328 */ 329 public void setGap(int gap) { 330 this.gap = gap; 331 } 332 333 /*** 334 * Returns the maxLabels. 335 * @return int 336 */ 337 public int getMaxLabels() { 338 return maxLabels; 339 } 340 341 void allocatePoints(Point2D.Double[] array) { 342 for (int i = 0; i < array.length; i++) 343 array[i] = new Point2D.Double(); 344 } 345 346 /*** 347 * Sets the maxLabels. 348 * @param maxLabels The maxLabels to set 349 */ 350 public void setMaxLabels(int maxLabels) { 351 this.maxLabels = maxLabels; 352 itemPosition = new Point2D.Double[maxLabels]; 353 allocatePoints(itemPosition); 354 linkPosition = new Point2D.Double[maxLabels]; 355 allocatePoints(linkPosition); 356 labelPosition = new Point2D.Double[maxLabels]; 357 allocatePoints(labelPosition); 358 left = new Point2D.Double[maxLabels]; 359 right = new Point2D.Double[maxLabels]; 360 } 361 362 /*** 363 * Returns the threshold. 364 * 365 * When the mouse moves a distance larger than this 366 * threshold since the last event, excentric labels 367 * are disabled. 368 * 369 * @return int 370 */ 371 public int getThreshold() { 372 return threshold; 373 } 374 375 /*** 376 * Sets the threshold. 377 * 378 * When the mouse moves a distance larger than the 379 * specified threshold since the last event, excentric 380 * labels are disabled. 381 * 382 * @param threshold The threshold to set 383 */ 384 public void setThreshold(int threshold) { 385 this.threshold = threshold; 386 } 387 /*** 388 * Returns the focusSize. 389 * @return int 390 */ 391 public int getFocusSize() { 392 return focusSize; 393 } 394 395 /*** 396 * Sets the focusSize. 397 * @param focusSize The focusSize to set 398 */ 399 public void setFocusSize(int focusSize) { 400 this.focusSize = focusSize; 401 cursorBounds = new Rectangle2D.Double(0, 0, focusSize, focusSize); 402 } 403 404 /*** 405 * Returns the backgroundColor. 406 * @return Color 407 */ 408 public Color getBackgroundColor() { 409 return backgroundColor; 410 } 411 412 /*** 413 * Returns the opaque. 414 * @return boolean 415 */ 416 public boolean isOpaque() { 417 return opaque; 418 } 419 420 /*** 421 * Sets the backgroundColor. 422 * @param backgroundColor The backgroundColor to set 423 */ 424 public void setBackgroundColor(Color backgroundColor) { 425 this.backgroundColor = backgroundColor; 426 } 427 428 /*** 429 * Sets the opaque. 430 * @param opaque The opaque to set 431 */ 432 public void setOpaque(boolean opaque) { 433 this.opaque = opaque; 434 } 435 436 }

This page was automatically generated by Maven