1:
62:
63: package ;
64:
65: import ;
66: import ;
67: import ;
68: import ;
69: import ;
70: import ;
71: import ;
72: import ;
73: import ;
74: import ;
75: import ;
76: import ;
77:
78: import ;
79: import ;
80: import ;
81: import ;
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94:
95:
104: public class XYDifferenceRenderer extends AbstractXYItemRenderer
105: implements XYItemRenderer,
106: Cloneable,
107: PublicCloneable,
108: Serializable {
109:
110:
111: private static final long serialVersionUID = -8447915602375584857L;
112:
113:
114: private transient Paint positivePaint;
115:
116:
117: private transient Paint negativePaint;
118:
119:
120: private boolean shapesVisible;
121:
122:
123: private transient Shape legendLine;
124:
125:
128: public XYDifferenceRenderer() {
129: this(Color.green, Color.red, false);
130: }
131:
132:
141: public XYDifferenceRenderer(Paint positivePaint, Paint negativePaint,
142: boolean shapes) {
143: if (positivePaint == null) {
144: throw new IllegalArgumentException(
145: "Null 'positivePaint' argument.");
146: }
147: if (negativePaint == null) {
148: throw new IllegalArgumentException(
149: "Null 'negativePaint' argument.");
150: }
151: this.positivePaint = positivePaint;
152: this.negativePaint = negativePaint;
153: this.shapesVisible = shapes;
154: this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
155: }
156:
157:
162: public Paint getPositivePaint() {
163: return this.positivePaint;
164: }
165:
166:
171: public void setPositivePaint(Paint paint) {
172: if (paint == null) {
173: throw new IllegalArgumentException("Null 'paint' argument.");
174: }
175: this.positivePaint = paint;
176: notifyListeners(new RendererChangeEvent(this));
177: }
178:
179:
184: public Paint getNegativePaint() {
185: return this.negativePaint;
186: }
187:
188:
193: public void setNegativePaint(Paint paint) {
194: if (paint == null) {
195: throw new IllegalArgumentException("Null 'paint' argument.");
196: }
197: this.negativePaint = paint;
198: notifyListeners(new RendererChangeEvent(this));
199: }
200:
201:
207: public boolean getShapesVisible() {
208: return this.shapesVisible;
209: }
210:
211:
217: public void setShapesVisible(boolean flag) {
218: this.shapesVisible = flag;
219: notifyListeners(new RendererChangeEvent(this));
220: }
221:
222:
227: public Shape getLegendLine() {
228: return this.legendLine;
229: }
230:
231:
237: public void setLegendLine(Shape line) {
238: if (line == null) {
239: throw new IllegalArgumentException("Null 'line' argument.");
240: }
241: this.legendLine = line;
242: notifyListeners(new RendererChangeEvent(this));
243: }
244:
245:
261: public XYItemRendererState initialise(Graphics2D g2,
262: Rectangle2D dataArea,
263: XYPlot plot,
264: XYDataset data,
265: PlotRenderingInfo info) {
266:
267: return super.initialise(g2, dataArea, plot, data, info);
268:
269: }
270:
271:
277: public int getPassCount() {
278: return 2;
279: }
280:
281:
299: public void drawItem(Graphics2D g2,
300: XYItemRendererState state,
301: Rectangle2D dataArea,
302: PlotRenderingInfo info,
303: XYPlot plot,
304: ValueAxis domainAxis,
305: ValueAxis rangeAxis,
306: XYDataset dataset,
307: int series,
308: int item,
309: CrosshairState crosshairState,
310: int pass) {
311:
312: if (pass == 0) {
313: drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis,
314: dataset, series, item, crosshairState);
315: }
316: else if (pass == 1) {
317: drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis,
318: dataset, series, item, crosshairState);
319: }
320:
321: }
322:
323:
339: protected void drawItemPass0(Graphics2D g2,
340: Rectangle2D dataArea,
341: PlotRenderingInfo info,
342: XYPlot plot,
343: ValueAxis domainAxis,
344: ValueAxis rangeAxis,
345: XYDataset dataset,
346: int series,
347: int item,
348: CrosshairState crosshairState) {
349:
350: if (series == 0) {
351:
352: PlotOrientation orientation = plot.getOrientation();
353: RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
354: RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
355:
356: double y0 = dataset.getYValue(0, item);
357: double x1 = dataset.getXValue(1, item);
358: double y1 = dataset.getYValue(1, item);
359:
360: double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
361: rangeAxisLocation);
362: double transX1 = domainAxis.valueToJava2D(x1, dataArea,
363: domainAxisLocation);
364: double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
365: rangeAxisLocation);
366:
367: if (item > 0) {
368: double prevx0 = dataset.getXValue(0, item - 1);
369: double prevy0 = dataset.getYValue(0, item - 1);
370: double prevy1 = dataset.getYValue(1, item - 1);
371:
372: double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea,
373: domainAxisLocation);
374: double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea,
375: rangeAxisLocation);
376: double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea,
377: rangeAxisLocation);
378:
379: Shape positive = getPositiveArea((float) prevtransX0,
380: (float) prevtransY0, (float) prevtransY1,
381: (float) transX1, (float) transY0, (float) transY1,
382: orientation);
383: if (positive != null) {
384: g2.setPaint(getPositivePaint());
385: g2.fill(positive);
386: }
387:
388: Shape negative = getNegativeArea((float) prevtransX0,
389: (float) prevtransY0, (float) prevtransY1,
390: (float) transX1, (float) transY0, (float) transY1,
391: orientation);
392:
393: if (negative != null) {
394: g2.setPaint(getNegativePaint());
395: g2.fill(negative);
396: }
397: }
398: }
399:
400: }
401:
402:
420: protected void drawItemPass1(Graphics2D g2,
421: Rectangle2D dataArea,
422: PlotRenderingInfo info,
423: XYPlot plot,
424: ValueAxis domainAxis,
425: ValueAxis rangeAxis,
426: XYDataset dataset,
427: int series,
428: int item,
429: CrosshairState crosshairState) {
430:
431: Shape entityArea = null;
432: EntityCollection entities = null;
433: if (info != null) {
434: entities = info.getOwner().getEntityCollection();
435: }
436:
437: Paint seriesPaint = getItemPaint(series, item);
438: Stroke seriesStroke = getItemStroke(series, item);
439: g2.setPaint(seriesPaint);
440: g2.setStroke(seriesStroke);
441:
442: if (series == 0) {
443:
444: PlotOrientation orientation = plot.getOrientation();
445: RectangleEdge domainAxisLocation = plot.getDomainAxisEdge();
446: RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();
447:
448: double x0 = dataset.getXValue(0, item);
449: double y0 = dataset.getYValue(0, item);
450: double x1 = dataset.getXValue(1, item);
451: double y1 = dataset.getYValue(1, item);
452:
453: double transX0 = domainAxis.valueToJava2D(x0, dataArea,
454: domainAxisLocation);
455: double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
456: rangeAxisLocation);
457: double transX1 = domainAxis.valueToJava2D(x1, dataArea,
458: domainAxisLocation);
459: double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
460: rangeAxisLocation);
461:
462: if (item > 0) {
463:
464: double prevx0 = dataset.getXValue(0, item - 1);
465: double prevy0 = dataset.getYValue(0, item - 1);
466: double prevx1 = dataset.getXValue(1, item - 1);
467: double prevy1 = dataset.getYValue(1, item - 1);
468:
469: double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea,
470: domainAxisLocation);
471: double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea,
472: rangeAxisLocation);
473: double prevtransX1 = domainAxis.valueToJava2D(prevx1, dataArea,
474: domainAxisLocation);
475: double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea,
476: rangeAxisLocation);
477:
478: Line2D line0 = null;
479: Line2D line1 = null;
480: if (orientation == PlotOrientation.HORIZONTAL) {
481: line0 = new Line2D.Double(transY0, transX0, prevtransY0,
482: prevtransX0);
483: line1 = new Line2D.Double(transY1, transX1, prevtransY1,
484: prevtransX1);
485: }
486: else if (orientation == PlotOrientation.VERTICAL) {
487: line0 = new Line2D.Double(transX0, transY0, prevtransX0,
488: prevtransY0);
489: line1 = new Line2D.Double(transX1, transY1, prevtransX1,
490: prevtransY1);
491: }
492: if (line0 != null && line0.intersects(dataArea)) {
493: g2.setPaint(getItemPaint(series, item));
494: g2.setStroke(getItemStroke(series, item));
495: g2.draw(line0);
496: }
497: if (line1 != null && line1.intersects(dataArea)) {
498: g2.setPaint(getItemPaint(1, item));
499: g2.setStroke(getItemStroke(1, item));
500: g2.draw(line1);
501: }
502: }
503:
504: if (getShapesVisible()) {
505: Shape shape0 = getItemShape(series, item);
506: if (orientation == PlotOrientation.HORIZONTAL) {
507: shape0 = ShapeUtilities.createTranslatedShape(shape0,
508: transY0, transX0);
509: }
510: else {
511: shape0 = ShapeUtilities.createTranslatedShape(shape0,
512: transX0, transY0);
513: }
514: if (shape0.intersects(dataArea)) {
515: g2.setPaint(getItemPaint(series, item));
516: g2.fill(shape0);
517: }
518: entityArea = shape0;
519:
520:
521: if (entities != null) {
522: if (entityArea == null) {
523: entityArea = new Rectangle2D.Double(transX0 - 2,
524: transY0 - 2, 4, 4);
525: }
526: String tip = null;
527: XYToolTipGenerator generator = getToolTipGenerator(series,
528: item);
529: if (generator != null) {
530: tip = generator.generateToolTip(dataset, series, item);
531: }
532: String url = null;
533: if (getURLGenerator() != null) {
534: url = getURLGenerator().generateURL(dataset, series,
535: item);
536: }
537: XYItemEntity entity = new XYItemEntity(entityArea, dataset,
538: series, item, tip, url);
539: entities.add(entity);
540: }
541:
542: Shape shape1 = getItemShape(series + 1, item);
543: if (orientation == PlotOrientation.HORIZONTAL) {
544: shape1 = ShapeUtilities.createTranslatedShape(shape1,
545: transY1, transX1);
546: }
547: else {
548: shape1 = ShapeUtilities.createTranslatedShape(shape1,
549: transX1, transY1);
550: }
551: if (shape1.intersects(dataArea)) {
552: g2.setPaint(getItemPaint(series + 1, item));
553: g2.fill(shape1);
554: }
555: entityArea = shape1;
556:
557:
558: if (entities != null) {
559: if (entityArea == null) {
560: entityArea = new Rectangle2D.Double(transX1 - 2,
561: transY1 - 2, 4, 4);
562: }
563: String tip = null;
564: XYToolTipGenerator generator = getToolTipGenerator(series,
565: item);
566: if (generator != null) {
567: tip = generator.generateToolTip(dataset, series + 1,
568: item);
569: }
570: String url = null;
571: if (getURLGenerator() != null) {
572: url = getURLGenerator().generateURL(dataset,
573: series + 1, item);
574: }
575: XYItemEntity entity = new XYItemEntity(entityArea, dataset,
576: series + 1, item, tip, url);
577: entities.add(entity);
578: }
579: }
580: updateCrosshairValues(crosshairState, x1, y1, transX1, transY1,
581: orientation);
582: }
583:
584: }
585:
586:
599: protected Shape getPositiveArea(float x0, float y0A, float y0B,
600: float x1, float y1A, float y1B,
601: PlotOrientation orientation) {
602:
603: Shape result = null;
604:
605: boolean startsNegative = (y0A >= y0B);
606: boolean endsNegative = (y1A >= y1B);
607: if (orientation == PlotOrientation.HORIZONTAL) {
608: startsNegative = (y0B >= y0A);
609: endsNegative = (y1B >= y1A);
610: }
611:
612: if (startsNegative) {
613: if (endsNegative) {
614:
615: result = null;
616: }
617: else {
618:
619: float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
620: GeneralPath area = new GeneralPath();
621: if (orientation == PlotOrientation.HORIZONTAL) {
622: area.moveTo(y1A, x1);
623: area.lineTo(p[1], p[0]);
624: area.lineTo(y1B, x1);
625: area.closePath();
626: }
627: else if (orientation == PlotOrientation.VERTICAL) {
628: area.moveTo(x1, y1A);
629: area.lineTo(p[0], p[1]);
630: area.lineTo(x1, y1B);
631: area.closePath();
632: }
633: result = area;
634: }
635: }
636: else {
637: if (endsNegative) {
638:
639: float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
640: GeneralPath area = new GeneralPath();
641: if (orientation == PlotOrientation.HORIZONTAL) {
642: area.moveTo(y0A, x0);
643: area.lineTo(p[1], p[0]);
644: area.lineTo(y0B, x0);
645: area.closePath();
646: }
647: else if (orientation == PlotOrientation.VERTICAL) {
648: area.moveTo(x0, y0A);
649: area.lineTo(p[0], p[1]);
650: area.lineTo(x0, y0B);
651: area.closePath();
652: }
653: result = area;
654:
655: }
656: else {
657: GeneralPath area = new GeneralPath();
658: if (orientation == PlotOrientation.HORIZONTAL) {
659: area.moveTo(y0A, x0);
660: area.lineTo(y1A, x1);
661: area.lineTo(y1B, x1);
662: area.lineTo(y0B, x0);
663: area.closePath();
664: }
665: else if (orientation == PlotOrientation.VERTICAL) {
666: area.moveTo(x0, y0A);
667: area.lineTo(x1, y1A);
668: area.lineTo(x1, y1B);
669: area.lineTo(x0, y0B);
670: area.closePath();
671: }
672: result = area;
673: }
674:
675: }
676:
677: return result;
678:
679: }
680:
681:
694: protected Shape getNegativeArea(float x0, float y0A, float y0B,
695: float x1, float y1A, float y1B,
696: PlotOrientation orientation) {
697:
698: Shape result = null;
699:
700: boolean startsNegative = (y0A >= y0B);
701: boolean endsNegative = (y1A >= y1B);
702: if (orientation == PlotOrientation.HORIZONTAL) {
703: startsNegative = (y0B >= y0A);
704: endsNegative = (y1B >= y1A);
705: }
706: if (startsNegative) {
707: if (endsNegative) {
708: GeneralPath area = new GeneralPath();
709: if (orientation == PlotOrientation.HORIZONTAL) {
710: area.moveTo(y0A, x0);
711: area.lineTo(y1A, x1);
712: area.lineTo(y1B, x1);
713: area.lineTo(y0B, x0);
714: area.closePath();
715: }
716: else if (orientation == PlotOrientation.VERTICAL) {
717: area.moveTo(x0, y0A);
718: area.lineTo(x1, y1A);
719: area.lineTo(x1, y1B);
720: area.lineTo(x0, y0B);
721: area.closePath();
722: }
723: result = area;
724: }
725: else {
726: float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
727: GeneralPath area = new GeneralPath();
728: if (orientation == PlotOrientation.HORIZONTAL) {
729: area.moveTo(y0A, x0);
730: area.lineTo(p[1], p[0]);
731: area.lineTo(y0B, x0);
732: area.closePath();
733: }
734: else if (orientation == PlotOrientation.VERTICAL) {
735: area.moveTo(x0, y0A);
736: area.lineTo(p[0], p[1]);
737: area.lineTo(x0, y0B);
738: area.closePath();
739: }
740: result = area;
741: }
742: }
743: else {
744: if (endsNegative) {
745:
746: float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B);
747: GeneralPath area = new GeneralPath();
748: if (orientation == PlotOrientation.HORIZONTAL) {
749: area.moveTo(y1A, x1);
750: area.lineTo(p[1], p[0]);
751: area.lineTo(y1B, x1);
752: area.closePath();
753: }
754: else if (orientation == PlotOrientation.VERTICAL) {
755: area.moveTo(x1, y1A);
756: area.lineTo(p[0], p[1]);
757: area.lineTo(x1, y1B);
758: area.closePath();
759: }
760: result = area;
761: }
762: else {
763:
764: }
765:
766: }
767:
768: return result;
769:
770: }
771:
772:
786: private float[] getIntersection(float x1, float y1, float x2, float y2,
787: float x3, float y3, float x4, float y4) {
788:
789: float n = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
790: float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
791: float u = n / d;
792:
793: float[] result = new float[2];
794: result[0] = x1 + u * (x2 - x1);
795: result[1] = y1 + u * (y2 - y1);
796: return result;
797:
798: }
799:
800:
809: public LegendItem getLegendItem(int datasetIndex, int series) {
810: LegendItem result = null;
811: XYPlot p = getPlot();
812: if (p != null) {
813: XYDataset dataset = p.getDataset(datasetIndex);
814: if (dataset != null) {
815: if (getItemVisible(series, 0)) {
816: String label = getLegendItemLabelGenerator().generateLabel(
817: dataset, series);
818: String description = label;
819: String toolTipText = null;
820: if (getLegendItemToolTipGenerator() != null) {
821: toolTipText
822: = getLegendItemToolTipGenerator().generateLabel(
823: dataset, series);
824: }
825: String urlText = null;
826: if (getLegendItemURLGenerator() != null) {
827: urlText = getLegendItemURLGenerator().generateLabel(
828: dataset, series);
829: }
830: Paint paint = getSeriesPaint(series);
831: Stroke stroke = getSeriesStroke(series);
832:
833: Line2D line = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
834: result = new LegendItem(label, description,
835: toolTipText, urlText, line, stroke, paint);
836: }
837: }
838:
839: }
840:
841: return result;
842:
843: }
844:
845:
852: public boolean equals(Object obj) {
853: if (obj == this) {
854: return true;
855: }
856: if (!(obj instanceof XYDifferenceRenderer)) {
857: return false;
858: }
859: if (!super.equals(obj)) {
860: return false;
861: }
862: XYDifferenceRenderer that = (XYDifferenceRenderer) obj;
863: if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) {
864: return false;
865: }
866: if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) {
867: return false;
868: }
869: if (this.shapesVisible != that.shapesVisible) {
870: return false;
871: }
872: if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
873: return false;
874: }
875: return true;
876: }
877:
878:
885: public Object clone() throws CloneNotSupportedException {
886: return super.clone();
887: }
888:
889:
896: private void writeObject(ObjectOutputStream stream) throws IOException {
897: stream.defaultWriteObject();
898: SerialUtilities.writePaint(this.positivePaint, stream);
899: SerialUtilities.writePaint(this.negativePaint, stream);
900: SerialUtilities.writeShape(this.legendLine, stream);
901: }
902:
903:
911: private void readObject(ObjectInputStream stream)
912: throws IOException, ClassNotFoundException {
913: stream.defaultReadObject();
914: this.positivePaint = SerialUtilities.readPaint(stream);
915: this.negativePaint = SerialUtilities.readPaint(stream);
916: this.legendLine = SerialUtilities.readShape(stream);
917: }
918:
919: }