1 63 64 package org.jfree.chart.renderer; 65 66 import java.awt.AlphaComposite ; 67 import java.awt.Color ; 68 import java.awt.Composite ; 69 import java.awt.Graphics2D ; 70 import java.awt.Paint ; 71 import java.awt.Shape ; 72 import java.awt.Stroke ; 73 import java.awt.geom.Line2D ; 74 import java.awt.geom.Rectangle2D ; 75 import java.io.IOException ; 76 import java.io.ObjectInputStream ; 77 import java.io.ObjectOutputStream ; 78 import java.io.Serializable ; 79 80 import org.jfree.chart.CrosshairInfo; 81 import org.jfree.chart.axis.ValueAxis; 82 import org.jfree.chart.entity.EntityCollection; 83 import org.jfree.chart.entity.XYItemEntity; 84 import org.jfree.chart.labels.HighLowToolTipGenerator; 85 import org.jfree.chart.labels.XYToolTipGenerator; 86 import org.jfree.chart.plot.PlotOrientation; 87 import org.jfree.chart.plot.PlotRenderingInfo; 88 import org.jfree.chart.plot.XYPlot; 89 import org.jfree.data.HighLowDataset; 90 import org.jfree.data.XYDataset; 91 import org.jfree.data.IntervalXYDataset; 92 import org.jfree.io.SerialUtilities; 93 import org.jfree.ui.RectangleEdge; 94 import org.jfree.util.PublicCloneable; 95 96 101 public class CandlestickRenderer extends AbstractXYItemRenderer 102 implements XYItemRenderer, 103 Cloneable , 104 PublicCloneable, 105 Serializable { 106 107 108 public static final int WIDTHMETHOD_AVERAGE = 0; 109 110 111 public static final int WIDTHMETHOD_SMALLEST = 1; 112 113 114 public static final int WIDTHMETHOD_INTERVALDATA = 2; 115 116 117 private int autoWidthMethod = WIDTHMETHOD_AVERAGE; 118 119 123 private double autoWidthFactor = 4.5 / 7; 124 125 126 private double autoWidthGap = 0.0; 127 128 129 private double candleWidth; 130 131 132 private double maxCandleWidthInMilliseconds = 1000.0 * 60.0 * 60.0 * 20.0; 133 134 135 private double maxCandleWidth; 136 137 138 private transient Paint upPaint; 139 140 141 private transient Paint downPaint; 142 143 144 private boolean drawVolume; 145 146 147 private transient double maxVolume; 148 149 152 public CandlestickRenderer() { 153 this(-1.0); 154 } 155 156 163 public CandlestickRenderer(double candleWidth) { 164 this(candleWidth, true, new HighLowToolTipGenerator()); 165 } 166 167 176 public CandlestickRenderer(double candleWidth, boolean drawVolume, 177 XYToolTipGenerator toolTipGenerator) { 178 179 super(); 180 setToolTipGenerator(toolTipGenerator); 181 this.candleWidth = candleWidth; 182 this.drawVolume = drawVolume; 183 this.upPaint = Color.green; 184 this.downPaint = Color.red; 185 186 } 187 188 194 public double getCandleWidth() { 195 return this.candleWidth; 196 } 197 198 210 public void setCandleWidth(double width) { 211 212 if (width != this.candleWidth) { 213 Double old = new Double (this.candleWidth); 214 this.candleWidth = width; 215 firePropertyChanged("CandleStickRenderer.candleWidth", old, new Double (width)); 216 } 217 218 } 219 220 225 public double getMaxCandleWidthInMilliseconds() { 226 return this.maxCandleWidthInMilliseconds; 227 } 228 229 238 public void setMaxCandleWidthInMilliseconds(double millis) { 239 240 this.maxCandleWidthInMilliseconds = millis; 241 firePropertyChanged("CandlestickRenderer.maxCandleWidthInMilliseconds", 242 null, new Double (millis)); 243 244 } 245 246 251 public int getAutoWidthMethod() { 252 return autoWidthMethod; 253 } 254 255 276 public void setAutoWidthMethod(int autoWidthMethod) { 277 if (this.autoWidthMethod != autoWidthMethod) { 278 Integer oldValue = new Integer (this.autoWidthMethod); 279 this.autoWidthMethod = autoWidthMethod; 280 firePropertyChanged("CandlestickRenderer.autoWidthMethod", 281 oldValue, new Integer (autoWidthMethod)); 282 } 283 } 284 285 291 public double getAutoWidthFactor() { 292 return autoWidthFactor; 293 } 294 295 305 public void setAutoWidthFactor(double autoWidthFactor) { 306 if (this.autoWidthFactor != autoWidthFactor) { 307 Double oldValue = new Double (this.autoWidthFactor); 308 this.autoWidthFactor = autoWidthFactor; 309 firePropertyChanged("CandlestickRenderer.autoWidthFactor", 310 oldValue, new Double (autoWidthFactor)); 311 } 312 } 313 314 320 public double getAutoWidthGap() { 321 return autoWidthGap; 322 } 323 324 334 public void setAutoWidthGap(double autoWidthGap) { 335 if (this.autoWidthGap != autoWidthGap) { 336 Double oldValue = new Double (this.autoWidthGap); 337 this.autoWidthGap = autoWidthGap; 338 firePropertyChanged("CandlestickRenderer.autoWidthGap", 339 oldValue, new Double (autoWidthGap)); 340 } 341 } 342 343 349 public Paint getUpPaint() { 350 return this.upPaint; 351 } 352 353 362 public void setUpPaint(Paint paint) { 363 364 Paint old = this.upPaint; 365 this.upPaint = paint; 366 firePropertyChanged("CandleStickRenderer.upPaint", old, paint); 367 368 } 369 370 376 public Paint getDownPaint() { 377 return this.downPaint; 378 } 379 380 389 public void setDownPaint(Paint paint) { 390 Paint old = this.downPaint; 391 this.downPaint = paint; 392 firePropertyChanged("CandleStickRenderer.downPaint", old, paint); 393 } 394 395 401 public boolean drawVolume() { 402 return this.drawVolume; 403 } 404 405 411 public void setDrawVolume(boolean flag) { 412 413 if (this.drawVolume != flag) { 414 this.drawVolume = flag; 415 firePropertyChanged("CandlestickRenderer.drawVolume", null, new Boolean (flag)); 416 } 417 418 } 419 420 434 public XYItemRendererState initialise(Graphics2D g2, 435 Rectangle2D dataArea, 436 XYPlot plot, 437 XYDataset dataset, 438 PlotRenderingInfo info) { 439 440 ValueAxis axis = plot.getDomainAxis(); 442 double x1 = axis.getLowerBound(); 443 double x2 = x1 + this.maxCandleWidthInMilliseconds; 444 RectangleEdge edge = plot.getDomainAxisEdge(); 445 double xx1 = axis.translateValueToJava2D(x1, dataArea, edge); 446 double xx2 = axis.translateValueToJava2D(x2, dataArea, edge); 447 this.maxCandleWidth = Math.abs(xx2 - xx1); 450 if (this.drawVolume) { 452 HighLowDataset highLowDataset = (HighLowDataset) dataset; 453 this.maxVolume = 0.0; 454 for (int series = 0; series < highLowDataset.getSeriesCount(); series++) { 455 for (int item = 0; item < highLowDataset.getItemCount(series); item++) { 456 double volume = highLowDataset.getVolumeValue(series, item).doubleValue(); 457 if (volume > this.maxVolume) { 458 this.maxVolume = volume; 459 } 460 461 } 462 } 463 } 464 465 return new XYItemRendererState(info); 466 } 467 468 484 public void drawItem(Graphics2D g2, 485 XYItemRendererState state, 486 Rectangle2D dataArea, 487 PlotRenderingInfo info, 488 XYPlot plot, 489 ValueAxis domainAxis, 490 ValueAxis rangeAxis, 491 XYDataset dataset, 492 int series, 493 int item, 494 CrosshairInfo crosshairInfo, 495 int pass) { 496 497 boolean horiz; 498 PlotOrientation orientation = plot.getOrientation(); 499 if (orientation == PlotOrientation.HORIZONTAL) { 500 horiz = true; 501 } 502 else if (orientation == PlotOrientation.VERTICAL) { 503 horiz = false; 504 } 505 else { 506 return; 507 } 508 509 EntityCollection entities = null; 511 if (info != null) { 512 entities = info.getOwner().getEntityCollection(); 513 } 514 515 HighLowDataset highLowData = (HighLowDataset) dataset; 516 517 Number x = highLowData.getXValue(series, item); 518 Number yHigh = highLowData.getHighValue(series, item); 519 Number yLow = highLowData.getLowValue(series, item); 520 Number yOpen = highLowData.getOpenValue(series, item); 521 Number yClose = highLowData.getCloseValue(series, item); 522 523 RectangleEdge domainEdge = plot.getDomainAxisEdge(); 524 double xx = domainAxis.translateValueToJava2D(x.doubleValue(), dataArea, domainEdge); 525 526 RectangleEdge edge = plot.getRangeAxisEdge(); 527 double yyHigh = rangeAxis.translateValueToJava2D(yHigh.doubleValue(), dataArea, edge); 528 double yyLow = rangeAxis.translateValueToJava2D(yLow.doubleValue(), dataArea, edge); 529 double yyOpen = rangeAxis.translateValueToJava2D(yOpen.doubleValue(), dataArea, edge); 530 double yyClose = rangeAxis.translateValueToJava2D(yClose.doubleValue(), dataArea, edge); 531 532 double volumeWidth; 533 double stickWidth; 534 if (candleWidth > 0) { 535 volumeWidth = candleWidth; 538 stickWidth = candleWidth; 539 } 540 else { 541 double xxWidth = 0; 542 int itemCount; 543 switch (autoWidthMethod) { 544 545 case WIDTHMETHOD_AVERAGE: 546 itemCount = highLowData.getItemCount(series); 547 if (horiz) { 548 xxWidth = dataArea.getHeight() / itemCount; 549 } 550 else { 551 xxWidth = dataArea.getWidth() / itemCount; 552 } 553 break; 554 555 case WIDTHMETHOD_SMALLEST: 556 itemCount = highLowData.getItemCount(series); 558 double lastPos = -1; 559 xxWidth = dataArea.getWidth(); 560 for (int i = 0; i < itemCount; i++) { 561 double pos = domainAxis.translateValueToJava2D( 562 highLowData.getXValue(series, i).doubleValue(), dataArea, domainEdge); 563 if (lastPos != -1) { 564 xxWidth = Math.min(xxWidth, Math.abs(pos - lastPos)); 565 } 566 lastPos = pos; 567 } 568 break; 569 570 case WIDTHMETHOD_INTERVALDATA: 571 IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; 572 double startPos = domainAxis.translateValueToJava2D( 573 intervalXYData.getStartXValue(series, item).doubleValue(), dataArea, 574 plot.getDomainAxisEdge()); 575 double endPos = domainAxis.translateValueToJava2D( 576 intervalXYData.getEndXValue(series, item).doubleValue(), dataArea, 577 plot.getDomainAxisEdge()); 578 xxWidth = Math.abs(endPos - startPos); 579 break; 580 581 } 582 xxWidth -= 2 * autoWidthGap; 583 xxWidth *= autoWidthFactor; 584 xxWidth = Math.min(xxWidth, maxCandleWidth); 585 volumeWidth = Math.max(Math.min(1, maxCandleWidth), xxWidth); 586 stickWidth = Math.max(Math.min(3, maxCandleWidth), xxWidth); 587 } 588 589 Paint p = getItemPaint(series, item); 590 Stroke s = getItemStroke(series, item); 591 592 g2.setStroke(s); 593 594 if (drawVolume) { 595 int volume = highLowData.getVolumeValue(series, item).intValue(); 596 double volumeHeight = volume / this.maxVolume; 597 598 double min, max; 599 if (horiz) { 600 min = dataArea.getMinX(); 601 max = dataArea.getMaxX(); 602 } 603 else { 604 min = dataArea.getMinY(); 605 max = dataArea.getMaxY(); 606 } 607 608 double zzVolume = volumeHeight * (max - min); 609 610 g2.setPaint(Color.gray); 611 Composite originalComposite = g2.getComposite(); 612 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)); 613 614 if (horiz) { 615 g2.fill(new Rectangle2D.Double (min, 616 xx - volumeWidth / 2, 617 zzVolume, volumeWidth)); 618 } 619 else { 620 g2.fill(new Rectangle2D.Double (xx - volumeWidth / 2, 621 max - zzVolume, volumeWidth, zzVolume)); 622 } 623 624 g2.setComposite(originalComposite); 625 } 626 627 g2.setPaint(p); 628 629 double yyMaxOpenClose = Math.max(yyOpen, yyClose); 630 double yyMinOpenClose = Math.min(yyOpen, yyClose); 631 double maxOpenClose = Math.max(yOpen.doubleValue(), yClose.doubleValue()); 632 double minOpenClose = Math.min(yOpen.doubleValue(), yClose.doubleValue()); 633 634 if (yHigh.doubleValue() > maxOpenClose) { 636 if (horiz) { 637 g2.draw(new Line2D.Double (yyHigh, xx, yyMaxOpenClose, xx)); 638 } 639 else { 640 g2.draw(new Line2D.Double (xx, yyHigh, xx, yyMaxOpenClose)); 641 } 642 } 643 644 if (yLow.doubleValue() < minOpenClose) { 646 if (horiz) { 647 g2.draw(new Line2D.Double (yyLow, xx, yyMinOpenClose, xx)); 648 } 649 else { 650 g2.draw(new Line2D.Double (xx, yyLow, xx, yyMinOpenClose)); 651 } 652 } 653 654 Shape body = null; 656 if (horiz) { 657 body = new Rectangle2D.Double (yyMinOpenClose, xx - stickWidth / 2, 658 yyMaxOpenClose - yyMinOpenClose, stickWidth); 659 } 660 else { 661 body = new Rectangle2D.Double (xx - stickWidth / 2, yyMinOpenClose, 662 stickWidth, yyMaxOpenClose - yyMinOpenClose); 663 } 664 if (yOpen.doubleValue() > yClose.doubleValue()) { 665 if (upPaint != null) { 666 g2.setPaint(upPaint); 667 g2.fill(body); 668 } 669 } 670 else { 671 if (downPaint != null) { 672 g2.setPaint(downPaint); 673 } 674 g2.fill(body); 675 } 676 g2.setPaint(p); 677 g2.draw(body); 678 679 if (entities != null) { 681 String tip = null; 682 if (getToolTipGenerator() != null) { 683 tip = getToolTipGenerator().generateToolTip(dataset, series, item); 684 } 685 String url = null; 686 if (getURLGenerator() != null) { 687 url = getURLGenerator().generateURL(dataset, series, item); 688 } 689 XYItemEntity entity = new XYItemEntity(body, dataset, series, item, tip, url); 690 entities.addEntity(entity); 691 } 692 693 } 694 695 702 public boolean equals(Object obj) { 703 704 if (obj == null) { 705 return false; 706 } 707 708 if (obj == this) { 709 return true; 710 } 711 712 if (obj instanceof CandlestickRenderer) { 713 CandlestickRenderer renderer = (CandlestickRenderer) obj; 714 boolean result = super.equals(obj); 715 result = result && (this.candleWidth == renderer.getCandleWidth()); 716 result = result && (this.upPaint.equals(renderer.getUpPaint())); 717 result = result && (this.downPaint.equals(renderer.getDownPaint())); 718 result = result && (this.drawVolume == renderer.drawVolume); 719 return result; 720 } 721 722 return false; 723 724 } 725 726 733 public Object clone() throws CloneNotSupportedException { 734 return super.clone(); 735 } 736 737 744 private void writeObject(ObjectOutputStream stream) throws IOException { 745 stream.defaultWriteObject(); 746 SerialUtilities.writePaint(this.upPaint, stream); 747 SerialUtilities.writePaint(this.downPaint, stream); 748 } 749 750 758 private void readObject(ObjectInputStream stream) throws IOException , ClassNotFoundException { 759 stream.defaultReadObject(); 760 this.upPaint = SerialUtilities.readPaint(stream); 761 this.downPaint = SerialUtilities.readPaint(stream); 762 } 763 764 } 765 | Popular Tags |