1 67 68 package org.jfree.chart.axis; 69 70 import java.awt.BasicStroke ; 71 import java.awt.Color ; 72 import java.awt.Font ; 73 import java.awt.Graphics2D ; 74 import java.awt.Paint ; 75 import java.awt.Shape ; 76 import java.awt.geom.Rectangle2D ; 77 import java.io.Serializable ; 78 import java.text.NumberFormat ; 79 import java.util.Arrays ; 80 import java.util.Iterator ; 81 import java.util.List ; 82 import java.util.Vector ; 83 84 import org.jfree.chart.event.AxisChangeEvent; 85 import org.jfree.chart.plot.Plot; 86 import org.jfree.chart.plot.PlotRenderingInfo; 87 import org.jfree.chart.plot.ValueAxisPlot; 88 import org.jfree.data.Range; 89 import org.jfree.text.TextUtilities; 90 import org.jfree.ui.RectangleEdge; 91 import org.jfree.ui.TextAnchor; 92 93 98 public class SymbolicAxis extends NumberAxis implements Serializable { 99 100 101 private static final long serialVersionUID = 7216330468770619716L; 102 103 104 public static final Paint DEFAULT_SYMBOLIC_GRID_LINE_PAINT 105 = new Color (232, 234, 232, 128); 106 107 108 private List symbolicValue; 109 110 111 private List symbolicGridLineList = null; 112 113 114 private transient Paint symbolicGridPaint; 115 116 117 private boolean symbolicGridLinesVisible; 118 119 127 public SymbolicAxis(String label, String [] sv) { 128 129 super(label); 130 131 this.symbolicValue = Arrays.asList(sv); 133 this.symbolicGridLinesVisible = true; 134 this.symbolicGridPaint = DEFAULT_SYMBOLIC_GRID_LINE_PAINT; 135 136 setAutoTickUnitSelection(false, false); 137 setAutoRangeStickyZero(false); 138 139 } 140 141 146 public String [] getSymbolicValue() { 147 148 String [] strToReturn = new String [this.symbolicValue.size()]; 149 strToReturn = (String []) this.symbolicValue.toArray(strToReturn); 150 return strToReturn; 151 } 152 153 158 public Paint getSymbolicGridPaint() { 159 return this.symbolicGridPaint; 160 } 161 162 169 public boolean isGridLinesVisible() { 170 return this.symbolicGridLinesVisible; 171 } 172 173 179 public void setSymbolicGridLinesVisible(boolean flag) { 180 181 if (this.symbolicGridLinesVisible != flag) { 182 this.symbolicGridLinesVisible = flag; 183 notifyListeners(new AxisChangeEvent(this)); 184 } 185 } 186 187 194 protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D drawArea, 195 Rectangle2D plotArea) { 196 throw new UnsupportedOperationException (); 197 } 198 199 215 public AxisState draw(Graphics2D g2, 216 double cursor, 217 Rectangle2D plotArea, 218 Rectangle2D dataArea, 219 RectangleEdge edge, 220 PlotRenderingInfo plotState) { 221 222 AxisState info = new AxisState(cursor); 223 if (isVisible()) { 224 info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState); 225 } 226 if (this.symbolicGridLinesVisible) { 227 drawSymbolicGridLines( 228 g2, plotArea, dataArea, edge, info.getTicks() 229 ); 230 } 231 return info; 232 233 } 234 235 249 public void drawSymbolicGridLines(Graphics2D g2, 250 Rectangle2D plotArea, 251 Rectangle2D dataArea, 252 RectangleEdge edge, 253 List ticks) { 254 255 Shape savedClip = g2.getClip(); 256 g2.clip(dataArea); 257 if (RectangleEdge.isTopOrBottom(edge)) { 258 drawSymbolicGridLinesHorizontal( 259 g2, plotArea, dataArea, true, ticks 260 ); 261 } 262 else if (RectangleEdge.isLeftOrRight(edge)) { 263 drawSymbolicGridLinesVertical(g2, plotArea, dataArea, true, ticks); 264 } 265 g2.setClip(savedClip); 266 267 } 268 269 287 public void drawSymbolicGridLinesHorizontal(Graphics2D g2, 288 Rectangle2D plotArea, 289 Rectangle2D dataArea, 290 boolean firstGridLineIsDark, 291 List ticks) { 292 293 this.symbolicGridLineList = new Vector (ticks.size()); 294 boolean currentGridLineIsDark = firstGridLineIsDark; 295 double yy = dataArea.getY(); 296 double xx1, xx2; 297 298 double outlineStrokeWidth; 300 if (getPlot().getOutlineStroke() != null) { 301 outlineStrokeWidth 302 = ((BasicStroke ) getPlot().getOutlineStroke()).getLineWidth(); 303 } 304 else { 305 outlineStrokeWidth = 1d; 306 } 307 308 Iterator iterator = ticks.iterator(); 309 ValueTick tick; 310 Rectangle2D symbolicGridLine; 311 while (iterator.hasNext()) { 312 tick = (ValueTick) iterator.next(); 313 xx1 = valueToJava2D( 314 tick.getValue() - 0.5d, dataArea, RectangleEdge.BOTTOM 315 ); 316 xx2 = valueToJava2D( 317 tick.getValue() + 0.5d, dataArea, RectangleEdge.BOTTOM 318 ); 319 if (currentGridLineIsDark) { 320 g2.setPaint(this.symbolicGridPaint); 321 } 323 else { 324 g2.setPaint(Color.white); 325 } 327 symbolicGridLine = new Rectangle2D.Double ( 328 xx1, yy + outlineStrokeWidth, 329 xx2 - xx1, dataArea.getMaxY() - yy - outlineStrokeWidth 330 ); 331 g2.fill(symbolicGridLine); 332 this.symbolicGridLineList.add(symbolicGridLine); 333 currentGridLineIsDark = !currentGridLineIsDark; 334 } 335 g2.setPaintMode(); 336 } 337 338 345 public Rectangle2D.Double getSymbolicGridLine(int position) { 346 347 if (this.symbolicGridLineList != null) { 348 return (Rectangle2D.Double ) this.symbolicGridLineList.get(position); 349 } 350 else { 351 return null; 352 } 353 354 } 355 356 359 protected void autoAdjustRange() { 360 361 Plot plot = getPlot(); 362 if (plot == null) { 363 return; } 365 366 if (plot instanceof ValueAxisPlot) { 367 368 double upper = this.symbolicValue.size() - 1; 370 double lower = 0; 371 double range = upper - lower; 372 373 double minRange = getAutoRangeMinimumSize(); 375 if (range < minRange) { 376 upper = (upper + lower + minRange) / 2; 377 lower = (upper + lower - minRange) / 2; 378 } 379 380 double upperMargin = 0.5; 383 double lowerMargin = 0.5; 384 385 if (getAutoRangeIncludesZero()) { 386 if (getAutoRangeStickyZero()) { 387 if (upper <= 0.0) { 388 upper = 0.0; 389 } 390 else { 391 upper = upper + upperMargin; 392 } 393 if (lower >= 0.0) { 394 lower = 0.0; 395 } 396 else { 397 lower = lower - lowerMargin; 398 } 399 } 400 else { 401 upper = Math.max(0.0, upper + upperMargin); 402 lower = Math.min(0.0, lower - lowerMargin); 403 } 404 } 405 else { 406 if (getAutoRangeStickyZero()) { 407 if (upper <= 0.0) { 408 upper = Math.min(0.0, upper + upperMargin); 409 } 410 else { 411 upper = upper + upperMargin * range; 412 } 413 if (lower >= 0.0) { 414 lower = Math.max(0.0, lower - lowerMargin); 415 } 416 else { 417 lower = lower - lowerMargin; 418 } 419 } 420 else { 421 upper = upper + upperMargin; 422 lower = lower - lowerMargin; 423 } 424 } 425 426 setRange(new Range(lower, upper), false, false); 427 428 } 429 430 } 431 432 445 public List refreshTicks(Graphics2D g2, 446 AxisState state, 447 Rectangle2D plotArea, 448 Rectangle2D dataArea, 449 RectangleEdge edge) { 450 451 List ticks = null; 452 if (RectangleEdge.isTopOrBottom(edge)) { 453 ticks = refreshTicksHorizontal( 454 g2, state.getCursor(), plotArea, dataArea, edge 455 ); 456 } 457 else if (RectangleEdge.isLeftOrRight(edge)) { 458 ticks = refreshTicksVertical( 459 g2, state.getCursor(), plotArea, dataArea, edge 460 ); 461 } 462 return ticks; 463 464 } 465 466 479 public List refreshTicksHorizontal(Graphics2D g2, double cursor, 480 Rectangle2D plotArea, 481 Rectangle2D dataArea, 482 RectangleEdge edge) { 483 484 List ticks = new java.util.ArrayList (); 485 486 Font tickLabelFont = getTickLabelFont(); 487 g2.setFont(tickLabelFont); 488 489 double size = getTickUnit().getSize(); 490 int count = calculateVisibleTickCount(); 491 double lowestTickValue = calculateLowestVisibleTickValue(); 492 493 double previousDrawnTickLabelPos = 0.0; 494 double previousDrawnTickLabelLength = 0.0; 495 496 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 497 for (int i = 0; i < count; i++) { 498 double currentTickValue = lowestTickValue + (i * size); 499 double xx = valueToJava2D(currentTickValue, dataArea, edge); 500 String tickLabel; 501 NumberFormat formatter = getNumberFormatOverride(); 502 if (formatter != null) { 503 tickLabel = formatter.format(currentTickValue); 504 } 505 else { 506 tickLabel = valueToString(currentTickValue); 507 } 508 509 Rectangle2D bounds = TextUtilities.getTextBounds( 511 tickLabel, g2, g2.getFontMetrics() 512 ); 513 double tickLabelLength = isVerticalTickLabels() 514 ? bounds.getHeight() : bounds.getWidth(); 515 boolean tickLabelsOverlapping = false; 516 if (i > 0) { 517 double avgTickLabelLength = (previousDrawnTickLabelLength 518 + tickLabelLength) / 2.0; 519 if (Math.abs(xx - previousDrawnTickLabelPos) 520 < avgTickLabelLength) { 521 tickLabelsOverlapping = true; 522 } 523 } 524 if (tickLabelsOverlapping) { 525 tickLabel = ""; } 527 else { 528 previousDrawnTickLabelPos = xx; 530 previousDrawnTickLabelLength = tickLabelLength; 531 } 532 533 TextAnchor anchor = null; 534 TextAnchor rotationAnchor = null; 535 double angle = 0.0; 536 if (isVerticalTickLabels()) { 537 anchor = TextAnchor.CENTER_RIGHT; 538 rotationAnchor = TextAnchor.CENTER_RIGHT; 539 if (edge == RectangleEdge.TOP) { 540 angle = Math.PI / 2.0; 541 } 542 else { 543 angle = -Math.PI / 2.0; 544 } 545 } 546 else { 547 if (edge == RectangleEdge.TOP) { 548 anchor = TextAnchor.BOTTOM_CENTER; 549 rotationAnchor = TextAnchor.BOTTOM_CENTER; 550 } 551 else { 552 anchor = TextAnchor.TOP_CENTER; 553 rotationAnchor = TextAnchor.TOP_CENTER; 554 } 555 } 556 Tick tick = new NumberTick( 557 new Double (currentTickValue), tickLabel, anchor, 558 rotationAnchor, angle 559 ); 560 ticks.add(tick); 561 } 562 } 563 return ticks; 564 565 } 566 567 579 public List refreshTicksVertical(Graphics2D g2, double cursor, 580 Rectangle2D plotArea, Rectangle2D dataArea, 581 RectangleEdge edge) { 582 583 List ticks = new java.util.ArrayList (); 584 585 Font tickLabelFont = getTickLabelFont(); 586 g2.setFont(tickLabelFont); 587 588 double size = getTickUnit().getSize(); 589 int count = calculateVisibleTickCount(); 590 double lowestTickValue = calculateLowestVisibleTickValue(); 591 592 double previousDrawnTickLabelPos = 0.0; 593 double previousDrawnTickLabelLength = 0.0; 594 595 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 596 for (int i = 0; i < count; i++) { 597 double currentTickValue = lowestTickValue + (i * size); 598 double yy = valueToJava2D(currentTickValue, dataArea, edge); 599 String tickLabel; 600 NumberFormat formatter = getNumberFormatOverride(); 601 if (formatter != null) { 602 tickLabel = formatter.format(currentTickValue); 603 } 604 else { 605 tickLabel = valueToString(currentTickValue); 606 } 607 608 Rectangle2D bounds = TextUtilities.getTextBounds( 610 tickLabel, g2, g2.getFontMetrics() 611 ); 612 double tickLabelLength = isVerticalTickLabels() 613 ? bounds.getWidth() : bounds.getHeight(); 614 boolean tickLabelsOverlapping = false; 615 if (i > 0) { 616 double avgTickLabelLength = 617 (previousDrawnTickLabelLength + tickLabelLength) / 2.0; 618 if (Math.abs(yy - previousDrawnTickLabelPos) 619 < avgTickLabelLength) { 620 tickLabelsOverlapping = true; 621 } 622 if (tickLabelsOverlapping) { 623 tickLabel = ""; } 625 else { 626 previousDrawnTickLabelPos = yy; 628 previousDrawnTickLabelLength = tickLabelLength; 629 } 630 } 631 TextAnchor anchor = null; 632 TextAnchor rotationAnchor = null; 633 double angle = 0.0; 634 if (isVerticalTickLabels()) { 635 anchor = TextAnchor.BOTTOM_CENTER; 636 rotationAnchor = TextAnchor.BOTTOM_CENTER; 637 if (edge == RectangleEdge.LEFT) { 638 angle = -Math.PI / 2.0; 639 } 640 else { 641 angle = Math.PI / 2.0; 642 } 643 } 644 else { 645 if (edge == RectangleEdge.LEFT) { 646 anchor = TextAnchor.CENTER_RIGHT; 647 rotationAnchor = TextAnchor.CENTER_RIGHT; 648 } 649 else { 650 anchor = TextAnchor.CENTER_LEFT; 651 rotationAnchor = TextAnchor.CENTER_LEFT; 652 } 653 } 654 Tick tick = new NumberTick( 655 new Double (currentTickValue), tickLabel, anchor, 656 rotationAnchor, angle 657 ); 658 ticks.add(tick); 659 } 660 } 661 return ticks; 662 663 } 664 665 672 public String valueToString(double value) { 673 674 String strToReturn; 675 try { 676 strToReturn = (String ) this.symbolicValue.get((int) value); 677 } 678 catch (IndexOutOfBoundsException ex) { 679 strToReturn = ""; 680 } 681 return strToReturn; 682 } 683 684 702 public void drawSymbolicGridLinesVertical(Graphics2D g2, 703 Rectangle2D drawArea, 704 Rectangle2D plotArea, 705 boolean firstGridLineIsDark, 706 List ticks) { 707 708 this.symbolicGridLineList = new Vector (ticks.size()); 709 boolean currentGridLineIsDark = firstGridLineIsDark; 710 double xx = plotArea.getX(); 711 double yy1, yy2; 712 713 double outlineStrokeWidth; 715 if (getPlot().getOutlineStroke() != null) { 716 outlineStrokeWidth 717 = ((BasicStroke ) getPlot().getOutlineStroke()).getLineWidth(); 718 } 719 else { 720 outlineStrokeWidth = 1d; 721 } 722 723 Iterator iterator = ticks.iterator(); 724 ValueTick tick; 725 Rectangle2D symbolicGridLine; 726 while (iterator.hasNext()) { 727 tick = (ValueTick) iterator.next(); 728 yy1 = valueToJava2D( 729 tick.getValue() + 0.5d, plotArea, RectangleEdge.LEFT 730 ); 731 yy2 = valueToJava2D( 732 tick.getValue() - 0.5d, plotArea, RectangleEdge.LEFT 733 ); 734 if (currentGridLineIsDark) { 735 g2.setPaint(this.symbolicGridPaint); 736 } 738 else { 739 g2.setPaint(Color.white); 740 } 742 symbolicGridLine = new Rectangle2D.Double (xx + outlineStrokeWidth, 743 yy1, plotArea.getMaxX() - xx - outlineStrokeWidth, yy2 - yy1); 744 g2.fill(symbolicGridLine); 745 this.symbolicGridLineList.add(symbolicGridLine); 746 currentGridLineIsDark = !currentGridLineIsDark; 747 } 748 g2.setPaintMode(); 749 } 750 751 } 752 | Popular Tags |