1 package prefuse.action.layout; 2 3 import java.awt.geom.Rectangle2D ; 4 import java.text.NumberFormat ; 5 import java.util.Iterator ; 6 import java.util.logging.Logger ; 7 8 import prefuse.Constants; 9 import prefuse.data.Schema; 10 import prefuse.data.query.ObjectRangeModel; 11 import prefuse.data.tuple.TupleSet; 12 import prefuse.data.util.Index; 13 import prefuse.util.MathLib; 14 import prefuse.util.PrefuseLib; 15 import prefuse.util.ui.ValuedRangeModel; 16 import prefuse.visual.VisualItem; 17 import prefuse.visual.VisualTable; 18 19 25 public class AxisLabelLayout extends Layout { 26 27 public static final String FRAC = "frac"; 28 public static final String LABEL = "_label"; 29 public static final String VALUE = "_value"; 30 31 private AxisLayout m_layout; private ValuedRangeModel m_model; 33 private double m_lo, m_hi, m_prevlo, m_prevhi; 34 35 private NumberFormat m_nf = NumberFormat.getInstance(); 36 private int m_axis; 37 private boolean m_asc = true; 38 private int m_scale = Constants.LINEAR_SCALE; 39 40 private double m_spacing; 42 49 public AxisLabelLayout(String group, int axis, ValuedRangeModel values) 50 { 51 this(group, axis, values, null); 52 } 53 54 62 public AxisLabelLayout(String group, int axis, ValuedRangeModel values, 63 Rectangle2D bounds) 64 { 65 super(group); 66 if ( bounds != null ) 67 setLayoutBounds(bounds); 68 m_model = values; 69 m_axis = axis; 70 m_spacing = 50; 71 } 72 73 79 public AxisLabelLayout(String group, AxisLayout layout) { 80 this(group, layout, null, 50); 81 } 82 83 90 public AxisLabelLayout(String group, AxisLayout layout, Rectangle2D bounds) { 91 this(group, layout, bounds, 50); 92 } 93 94 102 public AxisLabelLayout(String group, AxisLayout layout, Rectangle2D bounds, 103 double spacing) 104 { 105 super(group); 106 if ( bounds != null ) 107 setLayoutBounds(bounds); 108 m_layout = layout; 109 m_model = layout.getRangeModel(); 110 m_axis = layout.getAxis(); 111 m_scale = layout.getScale(); 112 m_spacing = spacing; 113 } 114 115 117 121 public NumberFormat getNumberFormat() { 122 return m_nf; 123 } 124 125 129 public void setNumberFormat(NumberFormat nf) { 130 m_nf = nf; 131 } 132 133 137 public double getSpacing() { 138 return m_spacing; 139 } 140 141 145 public void setSpacing(double spacing) { 146 m_spacing = spacing; 147 } 148 149 158 public int getScale() { 159 return m_scale; 160 } 161 162 171 public void setScale(int scale) { 172 if ( scale < 0 || scale >= Constants.SCALE_COUNT ) { 173 throw new IllegalArgumentException ( 174 "Unrecognized scale type: "+scale); 175 } 176 m_scale = scale; 177 } 178 179 185 public boolean isAscending() { 186 return m_asc; 187 } 188 189 196 public void setAscending(boolean asc) { 197 m_asc = asc; 198 } 199 200 204 public void setRangeModel(ValuedRangeModel model) { 205 m_model = model; 206 } 207 208 210 213 public void run(double frac) { 214 if ( m_model == null && m_layout != null ) 215 m_model = m_layout.getRangeModel(); 216 217 if ( m_model == null ) { 218 Logger.getLogger(this.getClass().getName()) 219 .warning("Axis labels missing a range model."); 220 return; 221 } 222 223 VisualTable labels = getTable(); 224 225 Double dfrac = (Double )labels.getClientProperty(FRAC); 228 double fr = dfrac==null ? 1.0 : dfrac.doubleValue(); 229 m_prevlo = m_prevlo + fr*(m_lo-m_prevlo); 230 m_prevhi = m_prevhi + fr*(m_hi-m_prevhi); 231 232 if ( m_model instanceof ObjectRangeModel ) 234 { m_lo = m_model.getValue(); 237 m_hi = m_lo + m_model.getExtent(); 238 239 ordinalLayout(labels); 241 } 242 else 243 { m_lo = ((Number )m_model.getLowValue()).doubleValue(); 246 m_hi = ((Number )m_model.getHighValue()).doubleValue(); 247 248 switch ( m_scale ) { 250 case Constants.LOG_SCALE: 251 logLayout(labels); 252 break; 253 case Constants.SQRT_SCALE: 254 sqrtLayout(labels); 255 break; 256 case Constants.LINEAR_SCALE: 257 default: 258 linearLayout(labels); 259 } 260 } 261 262 garbageCollect(labels); 264 } 265 266 269 272 protected void linearLayout(VisualTable labels) { 273 Rectangle2D b = getLayoutBounds(); 274 double breadth = getBreadth(b); 275 276 double span = m_hi-m_lo; 277 double pspan = m_prevhi-m_prevlo; 278 double vlo = 0; 279 if ( m_lo >= 0 ) { 280 vlo = Math.pow(10, Math.floor(MathLib.log10(m_lo))); 281 } else { 282 vlo = -Math.pow(10, 1+Math.floor(MathLib.log10(-m_lo))); 283 } 284 286 Iterator iter = labels.tuples(); 288 while ( iter.hasNext() ) { 289 VisualItem item = (VisualItem)iter.next(); 290 reset(item); 291 double v = item.getDouble(VALUE); 292 double x = span==0 ? 0 : ((v-m_lo)/span)*breadth; 293 set(item, x, b); 294 } 295 296 Index index = labels.index(VALUE); 297 double step = getLinearStep(span, span==0 ? 0 : breadth/span); 298 if ( step == 0 ) step = 1; 299 int r; 300 301 for ( double x, v=vlo; v<=m_hi; v+=step ) { 302 x = ((v-m_lo)/span)*breadth; 303 if ( x < -0.5 ) { 304 continue; 305 } else if ( (r=index.get(v)) >= 0 ) { 306 VisualItem item = labels.getItem(r); 307 item.setVisible(true); 308 item.setEndVisible(true); 309 } else { 310 VisualItem item = labels.addItem(); 311 item.set(LABEL, m_nf.format(v)); 312 item.setDouble(VALUE, v); 313 double f = pspan==0 ? 0 : ((v-m_prevlo)/pspan); 314 if ( f <= 0 || f >= 1.0 ) item.setStartVisible(true); 315 set(item, f*breadth, b); 316 set(item, x, b); 317 } 318 } 319 } 320 321 324 protected void sqrtLayout(VisualTable labels) { 325 Rectangle2D b = getLayoutBounds(); 326 double breadth = getBreadth(b); 327 328 double span = m_hi-m_lo; 329 double splo = MathLib.safeSqrt(m_prevlo); 330 double spspan = MathLib.safeSqrt(m_prevhi)-splo; 331 double vlo = Math.pow(10, Math.floor(MathLib.safeLog10(m_lo))); 332 double slo = MathLib.safeSqrt(m_lo); 333 double sspan = MathLib.safeSqrt(m_hi)-slo; 334 335 Iterator iter = labels.tuples(); 337 while ( iter.hasNext() ) { 338 VisualItem item = (VisualItem)iter.next(); 339 reset(item); 340 double v = item.getDouble(VALUE); 341 double x = span==0 ? 0 : ((MathLib.safeSqrt(v)-slo)/sspan)*breadth; 342 set(item, x, b); 343 } 344 345 Index index = labels.index(VALUE); 346 double step = getLinearStep(span, breadth/span); 347 if ( step == 0 ) step = 1; 348 int r; 349 for ( double x, v=vlo; v<=m_hi; v+=step ) { 350 x = ((MathLib.safeSqrt(v)-slo)/sspan)*breadth; 351 if ( x < -0.5 ) { 352 continue; 353 } else if ( (r=index.get(v)) >= 0 ) { 354 VisualItem item = labels.getItem(r); 355 item.setVisible(true); 356 item.setEndVisible(true); 357 } else { 358 VisualItem item = labels.addItem(); 359 item.set(LABEL, m_nf.format(v)); 360 item.setDouble(VALUE, v); 361 double f = spspan==0 ? 0 : ((MathLib.safeSqrt(v)-splo)/spspan); 362 if ( f <= 0 || f >= 1.0 ) { 363 item.setStartVisible(true); 364 } 365 set(item, f*breadth, b); 366 set(item, x, b); 367 } 368 } 369 } 370 371 375 protected void logLayout(VisualTable labels) { 376 Rectangle2D b = getLayoutBounds(); 377 double breadth = getBreadth(b); 378 379 labels.clear(); 380 381 double llo = MathLib.safeLog10(m_lo); 387 double lhi = MathLib.safeLog10(m_hi); 388 double lspan = lhi - llo; 389 390 double d = MathLib.log10(lhi-llo); 391 int e = (int)Math.floor(d); 392 int ilo = (int)Math.floor(llo); 393 int ihi = (int)Math.ceil(lhi); 394 395 double start = Math.pow(10,ilo); 396 double end = Math.pow(10, ihi); 397 double step = start * Math.pow(10, e); 398 400 for ( double val, v=start, i=0; v<=end; v+=step, ++i ) { 403 val = MathLib.safeLog10(v); 404 if ( i != 0 && Math.abs(val-Math.round(val)) < 0.0001 ) { 405 i = 0; 406 step = 10*step; 407 } 408 val = ((val-llo)/lspan)*breadth; 409 if ( val < -0.5 ) continue; 410 411 VisualItem item = labels.addItem(); 412 set(item, val, b); 413 String label = i==0 ? m_nf.format(v) : null; 414 item.set(LABEL, label); 415 item.setDouble(VALUE, v); 416 } 417 } 418 419 422 protected double getBreadth(Rectangle2D b) { 423 switch ( m_axis ) { 424 case Constants.X_AXIS: 425 return b.getWidth(); 426 default: 427 return b.getHeight(); 428 } 429 } 430 431 434 protected double adjust(double v) { 435 switch ( m_scale ) { 436 case Constants.LOG_SCALE: 437 return Math.pow(10,v); 438 case Constants.SQRT_SCALE: 439 return v*v; 440 case Constants.LINEAR_SCALE: 441 default: 442 return v; 443 } 444 } 445 446 449 protected double getLinearStep(double span, double scale) { 450 double log10 = Math.log(span)/Math.log(10); 451 double step = Math.pow(10, Math.floor(log10)); 452 453 double delta = step * scale / m_spacing; 454 if (delta > 20) { 455 step /= 20; 456 } else if (delta > 10) { 457 step /= 10; 458 } else if (delta > 5) { 459 step /= 5; 460 } else if (delta > 4) { 461 step /= 4; 462 } else if (delta > 2) { 463 step /= 2; 464 } else if (delta < 1) { 465 step *= 2; 466 } 467 return step; 468 } 469 470 473 476 protected void ordinalLayout(VisualTable labels) { 477 ObjectRangeModel model = (ObjectRangeModel)m_model; 478 double span = m_hi-m_lo; 479 double pspan = m_prevhi-m_prevlo; 480 481 Rectangle2D b = getLayoutBounds(); 482 double breadth = getBreadth(b); 483 double scale = breadth/span; 484 int step = getOrdinalStep(span, scale); 485 if ( step <= 0 ) step = 1; 486 487 Iterator iter = labels.tuples(); 489 while ( iter.hasNext() ) { 490 VisualItem item = (VisualItem)iter.next(); 491 reset(item); 492 double v = item.getDouble(VALUE); 493 double x = span==0 ? 0 : ((v-m_lo)/span)*breadth; 494 set(item, x, b); 495 } 496 497 Index index = labels.index(VALUE); 498 499 for ( int r, v=(int)m_lo; v<=m_hi; v+=step ) { 501 if ( (r=index.get((double)v)) >= 0 ) { 502 VisualItem item = labels.getItem(r); 503 item.set(VisualItem.LABEL, model.getObject(v).toString()); 504 item.setVisible(true); 505 item.setEndVisible(true); 506 } else { 507 VisualItem item = labels.addItem(); 508 item.set(VisualItem.LABEL, model.getObject(v).toString()); 509 item.setDouble(VisualItem.VALUE, v); 510 double f = pspan==0 ? 0 : ((v-m_prevlo)/pspan); 511 if ( f <= 0 || f >= 1.0 ) item.setStartVisible(true); 512 set(item, f*breadth, b); 513 set(item, (v-m_lo)*breadth/span, b); 514 } 515 } 516 } 517 518 521 protected int getOrdinalStep(double span, double scale) { 522 return (scale >= m_spacing ? 1 : (int)Math.ceil(m_spacing/scale)); 523 } 524 525 528 531 protected void set(VisualItem item, double xOrY, Rectangle2D b) { 532 switch ( m_axis ) { 533 case Constants.X_AXIS: 534 xOrY = m_asc ? xOrY + b.getMinX() : b.getMaxX() - xOrY; 535 PrefuseLib.updateDouble(item, VisualItem.X, xOrY); 536 PrefuseLib.updateDouble(item, VisualItem.Y, b.getMinY()); 537 PrefuseLib.updateDouble(item, VisualItem.X2, xOrY); 538 PrefuseLib.updateDouble(item, VisualItem.Y2, b.getMaxY()); 539 break; 540 case Constants.Y_AXIS: 541 xOrY = m_asc ? b.getMaxY() - xOrY - 1 : xOrY + b.getMinY(); 542 PrefuseLib.updateDouble(item, VisualItem.X, b.getMinX()); 543 PrefuseLib.updateDouble(item, VisualItem.Y, xOrY); 544 PrefuseLib.updateDouble(item, VisualItem.X2, b.getMaxX()); 545 PrefuseLib.updateDouble(item, VisualItem.Y2, xOrY); 546 } 547 } 548 549 552 protected void reset(VisualItem item) { 553 item.setVisible(false); 554 item.setEndVisible(false); 555 item.setStartStrokeColor(item.getStrokeColor()); 556 item.revertToDefault(VisualItem.STROKECOLOR); 557 item.revertToDefault(VisualItem.ENDSTROKECOLOR); 558 item.setStartTextColor(item.getTextColor()); 559 item.revertToDefault(VisualItem.TEXTCOLOR); 560 item.revertToDefault(VisualItem.ENDTEXTCOLOR); 561 item.setStartFillColor(item.getFillColor()); 562 item.revertToDefault(VisualItem.FILLCOLOR); 563 item.revertToDefault(VisualItem.ENDFILLCOLOR); 564 } 565 566 569 protected void garbageCollect(VisualTable labels) { 570 Iterator iter = labels.tuples(); 571 while ( iter.hasNext() ) { 572 VisualItem item = (VisualItem)iter.next(); 573 if ( !item.isStartVisible() && !item.isEndVisible() ) { 574 labels.removeTuple(item); 575 } 576 } 577 } 578 579 582 protected VisualTable getTable() { 583 TupleSet ts = m_vis.getGroup(m_group); 584 if ( ts == null ) { 585 Schema s = PrefuseLib.getAxisLabelSchema(); 586 VisualTable vt = m_vis.addTable(m_group, s); 587 vt.index(VALUE); 588 return vt; 589 } else if ( ts instanceof VisualTable ) { 590 return (VisualTable)ts; 591 } else { 592 throw new IllegalStateException ( 593 "Group already exists, not being used for labels"); 594 } 595 } 596 597 } | Popular Tags |