1:
58:
59: package ;
60:
61: import ;
62: import ;
63: import ;
64: import ;
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: import ;
78: import ;
79:
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:
93:
96: public class FastScatterPlot extends Plot implements ValueAxisPlot,
97: Zoomable,
98: Cloneable, Serializable {
99:
100:
101: private static final long serialVersionUID = 7871545897358563521L;
102:
103:
104: public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
105: BasicStroke.CAP_BUTT,
106: BasicStroke.JOIN_BEVEL,
107: 0.0f,
108: new float[] {2.0f, 2.0f},
109: 0.0f);
110:
111:
112: public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
113:
114:
115: private float[][] data;
116:
117:
118: private Range xDataRange;
119:
120:
121: private Range yDataRange;
122:
123:
124: private ValueAxis domainAxis;
125:
126:
127: private ValueAxis rangeAxis;
128:
129:
130: private transient Paint paint;
131:
132:
133: private boolean domainGridlinesVisible;
134:
135:
136: private transient Stroke domainGridlineStroke;
137:
138:
139: private transient Paint domainGridlinePaint;
140:
141:
142: private boolean rangeGridlinesVisible;
143:
144:
145: private transient Stroke rangeGridlineStroke;
146:
147:
148: private transient Paint rangeGridlinePaint;
149:
150:
151: protected static ResourceBundle localizationResources =
152: ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
153:
154:
157: public FastScatterPlot() {
158: this(null, null, null);
159: }
160:
161:
170: public FastScatterPlot(float[][] data,
171: ValueAxis domainAxis, ValueAxis rangeAxis) {
172:
173: super();
174:
175: this.data = data;
176: this.xDataRange = calculateXDataRange(data);
177: this.yDataRange = calculateYDataRange(data);
178: this.domainAxis = domainAxis;
179: if (domainAxis != null) {
180: domainAxis.setPlot(this);
181: domainAxis.addChangeListener(this);
182: }
183:
184: this.rangeAxis = rangeAxis;
185: if (rangeAxis != null) {
186: rangeAxis.setPlot(this);
187: rangeAxis.addChangeListener(this);
188: }
189:
190: this.paint = Color.red;
191:
192: this.domainGridlinesVisible = true;
193: this.domainGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
194: this.domainGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
195:
196: this.rangeGridlinesVisible = true;
197: this.rangeGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
198: this.rangeGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
199:
200: }
201:
202:
207: public String getPlotType() {
208: return localizationResources.getString("Fast_Scatter_Plot");
209: }
210:
211:
216: public float[][] getData() {
217: return this.data;
218: }
219:
220:
226: public void setData(float[][] data) {
227: this.data = data;
228: notifyListeners(new PlotChangeEvent(this));
229: }
230:
231:
236: public PlotOrientation getOrientation() {
237: return PlotOrientation.VERTICAL;
238: }
239:
240:
247: public ValueAxis getDomainAxis() {
248: return this.domainAxis;
249: }
250:
251:
258: public ValueAxis getRangeAxis() {
259: return this.rangeAxis;
260: }
261:
262:
267: public Paint getPaint() {
268: return this.paint;
269: }
270:
271:
277: public void setPaint(Paint paint) {
278: if (paint == null) {
279: throw new IllegalArgumentException("Null 'paint' argument.");
280: }
281: this.paint = paint;
282: notifyListeners(new PlotChangeEvent(this));
283: }
284:
285:
291: public boolean isDomainGridlinesVisible() {
292: return this.domainGridlinesVisible;
293: }
294:
295:
302: public void setDomainGridlinesVisible(boolean visible) {
303: if (this.domainGridlinesVisible != visible) {
304: this.domainGridlinesVisible = visible;
305: notifyListeners(new PlotChangeEvent(this));
306: }
307: }
308:
309:
315: public Stroke getDomainGridlineStroke() {
316: return this.domainGridlineStroke;
317: }
318:
319:
326: public void setDomainGridlineStroke(Stroke stroke) {
327: this.domainGridlineStroke = stroke;
328: notifyListeners(new PlotChangeEvent(this));
329: }
330:
331:
337: public Paint getDomainGridlinePaint() {
338: return this.domainGridlinePaint;
339: }
340:
341:
348: public void setDomainGridlinePaint(Paint paint) {
349: this.domainGridlinePaint = paint;
350: notifyListeners(new PlotChangeEvent(this));
351: }
352:
353:
359: public boolean isRangeGridlinesVisible() {
360: return this.rangeGridlinesVisible;
361: }
362:
363:
370: public void setRangeGridlinesVisible(boolean visible) {
371: if (this.rangeGridlinesVisible != visible) {
372: this.rangeGridlinesVisible = visible;
373: notifyListeners(new PlotChangeEvent(this));
374: }
375: }
376:
377:
383: public Stroke getRangeGridlineStroke() {
384: return this.rangeGridlineStroke;
385: }
386:
387:
394: public void setRangeGridlineStroke(Stroke stroke) {
395: this.rangeGridlineStroke = stroke;
396: notifyListeners(new PlotChangeEvent(this));
397: }
398:
399:
405: public Paint getRangeGridlinePaint() {
406: return this.rangeGridlinePaint;
407: }
408:
409:
416: public void setRangeGridlinePaint(Paint paint) {
417: this.rangeGridlinePaint = paint;
418: notifyListeners(new PlotChangeEvent(this));
419: }
420:
421:
433: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
434: PlotState parentState,
435: PlotRenderingInfo info) {
436:
437:
438: if (info != null) {
439: info.setPlotArea(area);
440: }
441:
442:
443: RectangleInsets insets = getInsets();
444: insets.trim(area);
445:
446: AxisSpace space = new AxisSpace();
447: space = this.domainAxis.reserveSpace(
448: g2, this, area, RectangleEdge.BOTTOM, space
449: );
450: space = this.rangeAxis.reserveSpace(
451: g2, this, area, RectangleEdge.LEFT, space
452: );
453: Rectangle2D dataArea = space.shrink(area, null);
454:
455: if (info != null) {
456: info.setDataArea(dataArea);
457: }
458:
459:
460: drawBackground(g2, dataArea);
461:
462: AxisState domainAxisState = null;
463: AxisState rangeAxisState = null;
464: if (this.domainAxis != null) {
465: domainAxisState = this.domainAxis.draw(
466: g2, dataArea.getMaxY(), area, dataArea,
467: RectangleEdge.BOTTOM, info
468: );
469: }
470: if (this.rangeAxis != null) {
471: rangeAxisState = this.rangeAxis.draw(
472: g2, dataArea.getMinX(), area, dataArea,
473: RectangleEdge.LEFT, info
474: );
475: }
476: drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
477: drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
478:
479: Shape originalClip = g2.getClip();
480: Composite originalComposite = g2.getComposite();
481:
482: g2.clip(dataArea);
483: g2.setComposite(
484: AlphaComposite.getInstance(
485: AlphaComposite.SRC_OVER, getForegroundAlpha()
486: )
487: );
488:
489: render(g2, dataArea, info, null);
490:
491: g2.setClip(originalClip);
492: g2.setComposite(originalComposite);
493: drawOutline(g2, dataArea);
494:
495: }
496:
497:
508: public void render(Graphics2D g2, Rectangle2D dataArea,
509: PlotRenderingInfo info, CrosshairState crosshairState) {
510:
511:
512:
513:
514: g2.setPaint(this.paint);
515:
516:
517:
518:
519:
520:
521:
522:
523:
524:
525:
526:
527:
528:
529: if (this.data != null) {
530: for (int i = 0; i < this.data[0].length; i++) {
531: float x = this.data[0][i];
532: float y = this.data[1][i];
533:
534:
535:
536: int transX = (int) this.domainAxis.valueToJava2D(
537: x, dataArea, RectangleEdge.BOTTOM
538: );
539: int transY = (int) this.rangeAxis.valueToJava2D(
540: y, dataArea, RectangleEdge.LEFT
541: );
542: g2.fillRect(transX, transY, 1, 1);
543: }
544: }
545:
546:
547:
548:
549: }
550:
551:
558: protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea,
559: List ticks) {
560:
561:
562: if (isDomainGridlinesVisible()) {
563: Stroke gridStroke = getDomainGridlineStroke();
564: Paint gridPaint = getDomainGridlinePaint();
565: if ((gridStroke != null) && (gridPaint != null)) {
566: Iterator iterator = ticks.iterator();
567: while (iterator.hasNext()) {
568: ValueTick tick = (ValueTick) iterator.next();
569: double v = this.domainAxis.valueToJava2D(
570: tick.getValue(), dataArea, RectangleEdge.BOTTOM
571: );
572: Line2D line = new Line2D.Double(
573: v, dataArea.getMinY(), v, dataArea.getMaxY()
574: );
575: g2.setPaint(gridPaint);
576: g2.setStroke(gridStroke);
577: g2.draw(line);
578: }
579: }
580: }
581: }
582:
583:
590: protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea,
591: List ticks) {
592:
593:
594: if (isRangeGridlinesVisible()) {
595: Stroke gridStroke = getRangeGridlineStroke();
596: Paint gridPaint = getRangeGridlinePaint();
597: if ((gridStroke != null) && (gridPaint != null)) {
598: Iterator iterator = ticks.iterator();
599: while (iterator.hasNext()) {
600: ValueTick tick = (ValueTick) iterator.next();
601: double v = this.rangeAxis.valueToJava2D(
602: tick.getValue(), dataArea, RectangleEdge.LEFT
603: );
604: Line2D line = new Line2D.Double(
605: dataArea.getMinX(), v, dataArea.getMaxX(), v
606: );
607: g2.setPaint(gridPaint);
608: g2.setStroke(gridStroke);
609: g2.draw(line);
610: }
611: }
612: }
613:
614: }
615:
616:
623: public Range getDataRange(ValueAxis axis) {
624:
625: Range result = null;
626: if (axis == this.domainAxis) {
627: result = this.xDataRange;
628: }
629: else if (axis == this.rangeAxis) {
630: result = this.yDataRange;
631: }
632: return result;
633: }
634:
635:
642: private Range calculateXDataRange(float[][] data) {
643:
644: Range result = null;
645:
646: if (data != null) {
647: float lowest = Float.POSITIVE_INFINITY;
648: float highest = Float.NEGATIVE_INFINITY;
649: for (int i = 0; i < data[0].length; i++) {
650: float v = data[0][i];
651: if (v < lowest) {
652: lowest = v;
653: }
654: if (v > highest) {
655: highest = v;
656: }
657: }
658: if (lowest <= highest) {
659: result = new Range(lowest, highest);
660: }
661: }
662:
663: return result;
664:
665: }
666:
667:
674: private Range calculateYDataRange(float[][] data) {
675:
676: Range result = null;
677:
678: if (data != null) {
679: float lowest = Float.POSITIVE_INFINITY;
680: float highest = Float.NEGATIVE_INFINITY;
681: for (int i = 0; i < data[0].length; i++) {
682: float v = data[1][i];
683: if (v < lowest) {
684: lowest = v;
685: }
686: if (v > highest) {
687: highest = v;
688: }
689: }
690: if (lowest <= highest) {
691: result = new Range(lowest, highest);
692: }
693: }
694: return result;
695:
696: }
697:
698:
705: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
706: Point2D source) {
707: this.domainAxis.resizeRange(factor);
708: }
709:
710:
720: public void zoomDomainAxes(double lowerPercent, double upperPercent,
721: PlotRenderingInfo info, Point2D source) {
722: this.domainAxis.zoomRange(lowerPercent, upperPercent);
723: }
724:
725:
732: public void zoomRangeAxes(double factor,
733: PlotRenderingInfo info, Point2D source) {
734: this.rangeAxis.resizeRange(factor);
735: }
736:
737:
747: public void zoomRangeAxes(double lowerPercent, double upperPercent,
748: PlotRenderingInfo info, Point2D source) {
749: this.rangeAxis.zoomRange(lowerPercent, upperPercent);
750: }
751:
752:
757: public boolean isDomainZoomable() {
758: return true;
759: }
760:
761:
766: public boolean isRangeZoomable() {
767: return true;
768: }
769:
770:
777: public boolean equals(Object obj) {
778: if (obj == this) {
779: return true;
780: }
781: if (!super.equals(obj)) {
782: return false;
783: }
784: if (!(obj instanceof FastScatterPlot)) {
785: return false;
786: }
787: FastScatterPlot that = (FastScatterPlot) obj;
788: if (!ArrayUtilities.equal(this.data, that.data)) {
789: return false;
790: }
791: if (!ObjectUtilities.equal(this.domainAxis, that.domainAxis)) {
792: return false;
793: }
794: if (!ObjectUtilities.equal(this.rangeAxis, that.rangeAxis)) {
795: return false;
796: }
797: if (!PaintUtilities.equal(this.paint, that.paint)) {
798: return false;
799: }
800: if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
801: return false;
802: }
803: if (!PaintUtilities.equal(this.domainGridlinePaint,
804: that.domainGridlinePaint)) {
805: return false;
806: }
807: if (!ObjectUtilities.equal(this.domainGridlineStroke,
808: that.domainGridlineStroke)) {
809: return false;
810: }
811: if (!this.rangeGridlinesVisible == that.rangeGridlinesVisible) {
812: return false;
813: }
814: if (!PaintUtilities.equal(this.rangeGridlinePaint,
815: that.rangeGridlinePaint)) {
816: return false;
817: }
818: if (!ObjectUtilities.equal(this.rangeGridlineStroke,
819: that.rangeGridlineStroke)) {
820: return false;
821: }
822: return true;
823: }
824:
825:
833: public Object clone() throws CloneNotSupportedException {
834:
835: FastScatterPlot clone = (FastScatterPlot) super.clone();
836:
837: if (this.data != null) {
838: clone.data = ArrayUtilities.clone(this.data);
839: }
840:
841: if (this.domainAxis != null) {
842: clone.domainAxis = (ValueAxis) this.domainAxis.clone();
843: clone.domainAxis.setPlot(clone);
844: clone.domainAxis.addChangeListener(clone);
845: }
846:
847: if (this.rangeAxis != null) {
848: clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
849: clone.rangeAxis.setPlot(clone);
850: clone.rangeAxis.addChangeListener(clone);
851: }
852:
853: return clone;
854:
855: }
856:
857:
864: private void writeObject(ObjectOutputStream stream) throws IOException {
865: stream.defaultWriteObject();
866: SerialUtilities.writePaint(this.paint, stream);
867: SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
868: SerialUtilities.writePaint(this.domainGridlinePaint, stream);
869: SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
870: SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
871: }
872:
873:
881: private void readObject(ObjectInputStream stream)
882: throws IOException, ClassNotFoundException {
883: stream.defaultReadObject();
884:
885: this.paint = SerialUtilities.readPaint(stream);
886: this.domainGridlineStroke = SerialUtilities.readStroke(stream);
887: this.domainGridlinePaint = SerialUtilities.readPaint(stream);
888:
889: this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
890: this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
891:
892: if (this.domainAxis != null) {
893: this.domainAxis.addChangeListener(this);
894: }
895:
896: if (this.rangeAxis != null) {
897: this.rangeAxis.addChangeListener(this);
898: }
899: }
900:
901: }