1:
61:
62: package ;
63:
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: import ;
80: import ;
81: import ;
82:
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96:
97:
100: public class FastScatterPlot extends Plot implements ValueAxisPlot,
101: Zoomable,
102: Cloneable, Serializable {
103:
104:
105: private static final long serialVersionUID = 7871545897358563521L;
106:
107:
108: public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
109: BasicStroke.CAP_BUTT,
110: BasicStroke.JOIN_BEVEL,
111: 0.0f,
112: new float[] {2.0f, 2.0f},
113: 0.0f);
114:
115:
116: public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
117:
118:
119: private float[][] data;
120:
121:
122: private Range xDataRange;
123:
124:
125: private Range yDataRange;
126:
127:
128: private ValueAxis domainAxis;
129:
130:
131: private ValueAxis rangeAxis;
132:
133:
134: private transient Paint paint;
135:
136:
137: private boolean domainGridlinesVisible;
138:
139:
140: private transient Stroke domainGridlineStroke;
141:
142:
143: private transient Paint domainGridlinePaint;
144:
145:
146: private boolean rangeGridlinesVisible;
147:
148:
149: private transient Stroke rangeGridlineStroke;
150:
151:
152: private transient Paint rangeGridlinePaint;
153:
154:
155: protected static ResourceBundle localizationResources =
156: ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
157:
158:
162: public FastScatterPlot() {
163: this(null, new NumberAxis("X"), new NumberAxis("Y"));
164: }
165:
166:
175: public FastScatterPlot(float[][] data,
176: ValueAxis domainAxis, ValueAxis rangeAxis) {
177:
178: super();
179: if (domainAxis == null) {
180: throw new IllegalArgumentException("Null 'domainAxis' argument.");
181: }
182: if (rangeAxis == null) {
183: throw new IllegalArgumentException("Null 'rangeAxis' argument.");
184: }
185:
186: this.data = data;
187: this.xDataRange = calculateXDataRange(data);
188: this.yDataRange = calculateYDataRange(data);
189: this.domainAxis = domainAxis;
190: this.domainAxis.setPlot(this);
191: this.domainAxis.addChangeListener(this);
192: this.rangeAxis = rangeAxis;
193: this.rangeAxis.setPlot(this);
194: this.rangeAxis.addChangeListener(this);
195:
196: this.paint = Color.red;
197:
198: this.domainGridlinesVisible = true;
199: this.domainGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
200: this.domainGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
201:
202: this.rangeGridlinesVisible = true;
203: this.rangeGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
204: this.rangeGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
205:
206: }
207:
208:
213: public String getPlotType() {
214: return localizationResources.getString("Fast_Scatter_Plot");
215: }
216:
217:
224: public float[][] getData() {
225: return this.data;
226: }
227:
228:
236: public void setData(float[][] data) {
237: this.data = data;
238: notifyListeners(new PlotChangeEvent(this));
239: }
240:
241:
246: public PlotOrientation getOrientation() {
247: return PlotOrientation.VERTICAL;
248: }
249:
250:
257: public ValueAxis getDomainAxis() {
258: return this.domainAxis;
259: }
260:
261:
271: public void setDomainAxis(ValueAxis axis) {
272: if (axis == null) {
273: throw new IllegalArgumentException("Null 'axis' argument.");
274: }
275: this.domainAxis = axis;
276: notifyListeners(new PlotChangeEvent(this));
277: }
278:
279:
286: public ValueAxis getRangeAxis() {
287: return this.rangeAxis;
288: }
289:
290:
300: public void setRangeAxis(ValueAxis axis) {
301: if (axis == null) {
302: throw new IllegalArgumentException("Null 'axis' argument.");
303: }
304: this.rangeAxis = axis;
305: notifyListeners(new PlotChangeEvent(this));
306: }
307:
308:
316: public Paint getPaint() {
317: return this.paint;
318: }
319:
320:
328: public void setPaint(Paint paint) {
329: if (paint == null) {
330: throw new IllegalArgumentException("Null 'paint' argument.");
331: }
332: this.paint = paint;
333: notifyListeners(new PlotChangeEvent(this));
334: }
335:
336:
345: public boolean isDomainGridlinesVisible() {
346: return this.domainGridlinesVisible;
347: }
348:
349:
358: public void setDomainGridlinesVisible(boolean visible) {
359: if (this.domainGridlinesVisible != visible) {
360: this.domainGridlinesVisible = visible;
361: notifyListeners(new PlotChangeEvent(this));
362: }
363: }
364:
365:
373: public Stroke getDomainGridlineStroke() {
374: return this.domainGridlineStroke;
375: }
376:
377:
385: public void setDomainGridlineStroke(Stroke stroke) {
386: if (stroke == null) {
387: throw new IllegalArgumentException("Null 'stroke' argument.");
388: }
389: this.domainGridlineStroke = stroke;
390: notifyListeners(new PlotChangeEvent(this));
391: }
392:
393:
401: public Paint getDomainGridlinePaint() {
402: return this.domainGridlinePaint;
403: }
404:
405:
413: public void setDomainGridlinePaint(Paint paint) {
414: if (paint == null) {
415: throw new IllegalArgumentException("Null 'paint' argument.");
416: }
417: this.domainGridlinePaint = paint;
418: notifyListeners(new PlotChangeEvent(this));
419: }
420:
421:
429: public boolean isRangeGridlinesVisible() {
430: return this.rangeGridlinesVisible;
431: }
432:
433:
442: public void setRangeGridlinesVisible(boolean visible) {
443: if (this.rangeGridlinesVisible != visible) {
444: this.rangeGridlinesVisible = visible;
445: notifyListeners(new PlotChangeEvent(this));
446: }
447: }
448:
449:
457: public Stroke getRangeGridlineStroke() {
458: return this.rangeGridlineStroke;
459: }
460:
461:
469: public void setRangeGridlineStroke(Stroke stroke) {
470: if (stroke == null) {
471: throw new IllegalArgumentException("Null 'stroke' argument.");
472: }
473: this.rangeGridlineStroke = stroke;
474: notifyListeners(new PlotChangeEvent(this));
475: }
476:
477:
485: public Paint getRangeGridlinePaint() {
486: return this.rangeGridlinePaint;
487: }
488:
489:
497: public void setRangeGridlinePaint(Paint paint) {
498: if (paint == null) {
499: throw new IllegalArgumentException("Null 'paint' argument.");
500: }
501: this.rangeGridlinePaint = paint;
502: notifyListeners(new PlotChangeEvent(this));
503: }
504:
505:
517: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
518: PlotState parentState,
519: PlotRenderingInfo info) {
520:
521:
522: if (info != null) {
523: info.setPlotArea(area);
524: }
525:
526:
527: RectangleInsets insets = getInsets();
528: insets.trim(area);
529:
530: AxisSpace space = new AxisSpace();
531: space = this.domainAxis.reserveSpace(g2, this, area,
532: RectangleEdge.BOTTOM, space);
533: space = this.rangeAxis.reserveSpace(g2, this, area, RectangleEdge.LEFT,
534: space);
535: Rectangle2D dataArea = space.shrink(area, null);
536:
537: if (info != null) {
538: info.setDataArea(dataArea);
539: }
540:
541:
542: drawBackground(g2, dataArea);
543:
544: AxisState domainAxisState = this.domainAxis.draw(g2,
545: dataArea.getMaxY(), area, dataArea, RectangleEdge.BOTTOM, info);
546: AxisState rangeAxisState = this.rangeAxis.draw(g2, dataArea.getMinX(),
547: area, dataArea, RectangleEdge.LEFT, info);
548: drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
549: drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
550:
551: Shape originalClip = g2.getClip();
552: Composite originalComposite = g2.getComposite();
553:
554: g2.clip(dataArea);
555: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
556: getForegroundAlpha()));
557:
558: render(g2, dataArea, info, null);
559:
560: g2.setClip(originalClip);
561: g2.setComposite(originalComposite);
562: drawOutline(g2, dataArea);
563:
564: }
565:
566:
577: public void render(Graphics2D g2, Rectangle2D dataArea,
578: PlotRenderingInfo info, CrosshairState crosshairState) {
579:
580:
581:
582:
583: g2.setPaint(this.paint);
584:
585:
586:
587:
588:
589:
590:
591:
592:
593:
594:
595:
596:
597:
598: if (this.data != null) {
599: for (int i = 0; i < this.data[0].length; i++) {
600: float x = this.data[0][i];
601: float y = this.data[1][i];
602:
603:
604:
605: int transX = (int) this.domainAxis.valueToJava2D(x, dataArea,
606: RectangleEdge.BOTTOM);
607: int transY = (int) this.rangeAxis.valueToJava2D(y, dataArea,
608: RectangleEdge.LEFT);
609: g2.fillRect(transX, transY, 1, 1);
610: }
611: }
612:
613:
614:
615:
616: }
617:
618:
625: protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea,
626: List ticks) {
627:
628:
629: if (isDomainGridlinesVisible()) {
630: Iterator iterator = ticks.iterator();
631: while (iterator.hasNext()) {
632: ValueTick tick = (ValueTick) iterator.next();
633: double v = this.domainAxis.valueToJava2D(tick.getValue(),
634: dataArea, RectangleEdge.BOTTOM);
635: Line2D line = new Line2D.Double(v, dataArea.getMinY(), v,
636: dataArea.getMaxY());
637: g2.setPaint(getDomainGridlinePaint());
638: g2.setStroke(getDomainGridlineStroke());
639: g2.draw(line);
640: }
641: }
642: }
643:
644:
651: protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea,
652: List ticks) {
653:
654:
655: if (isRangeGridlinesVisible()) {
656: Iterator iterator = ticks.iterator();
657: while (iterator.hasNext()) {
658: ValueTick tick = (ValueTick) iterator.next();
659: double v = this.rangeAxis.valueToJava2D(tick.getValue(),
660: dataArea, RectangleEdge.LEFT);
661: Line2D line = new Line2D.Double(dataArea.getMinX(), v,
662: dataArea.getMaxX(), v);
663: g2.setPaint(getRangeGridlinePaint());
664: g2.setStroke(getRangeGridlineStroke());
665: g2.draw(line);
666: }
667: }
668:
669: }
670:
671:
680: public Range getDataRange(ValueAxis axis) {
681: Range result = null;
682: if (axis == this.domainAxis) {
683: result = this.xDataRange;
684: }
685: else if (axis == this.rangeAxis) {
686: result = this.yDataRange;
687: }
688: return result;
689: }
690:
691:
698: private Range calculateXDataRange(float[][] data) {
699:
700: Range result = null;
701:
702: if (data != null) {
703: float lowest = Float.POSITIVE_INFINITY;
704: float highest = Float.NEGATIVE_INFINITY;
705: for (int i = 0; i < data[0].length; i++) {
706: float v = data[0][i];
707: if (v < lowest) {
708: lowest = v;
709: }
710: if (v > highest) {
711: highest = v;
712: }
713: }
714: if (lowest <= highest) {
715: result = new Range(lowest, highest);
716: }
717: }
718:
719: return result;
720:
721: }
722:
723:
730: private Range calculateYDataRange(float[][] data) {
731:
732: Range result = null;
733:
734: if (data != null) {
735: float lowest = Float.POSITIVE_INFINITY;
736: float highest = Float.NEGATIVE_INFINITY;
737: for (int i = 0; i < data[0].length; i++) {
738: float v = data[1][i];
739: if (v < lowest) {
740: lowest = v;
741: }
742: if (v > highest) {
743: highest = v;
744: }
745: }
746: if (lowest <= highest) {
747: result = new Range(lowest, highest);
748: }
749: }
750: return result;
751:
752: }
753:
754:
761: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
762: Point2D source) {
763: this.domainAxis.resizeRange(factor);
764: }
765:
766:
776: public void zoomDomainAxes(double lowerPercent, double upperPercent,
777: PlotRenderingInfo info, Point2D source) {
778: this.domainAxis.zoomRange(lowerPercent, upperPercent);
779: }
780:
781:
788: public void zoomRangeAxes(double factor,
789: PlotRenderingInfo info, Point2D source) {
790: this.rangeAxis.resizeRange(factor);
791: }
792:
793:
803: public void zoomRangeAxes(double lowerPercent, double upperPercent,
804: PlotRenderingInfo info, Point2D source) {
805: this.rangeAxis.zoomRange(lowerPercent, upperPercent);
806: }
807:
808:
813: public boolean isDomainZoomable() {
814: return true;
815: }
816:
817:
822: public boolean isRangeZoomable() {
823: return true;
824: }
825:
826:
833: public boolean equals(Object obj) {
834: if (obj == this) {
835: return true;
836: }
837: if (!super.equals(obj)) {
838: return false;
839: }
840: if (!(obj instanceof FastScatterPlot)) {
841: return false;
842: }
843: FastScatterPlot that = (FastScatterPlot) obj;
844: if (!ArrayUtilities.equal(this.data, that.data)) {
845: return false;
846: }
847: if (!ObjectUtilities.equal(this.domainAxis, that.domainAxis)) {
848: return false;
849: }
850: if (!ObjectUtilities.equal(this.rangeAxis, that.rangeAxis)) {
851: return false;
852: }
853: if (!PaintUtilities.equal(this.paint, that.paint)) {
854: return false;
855: }
856: if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
857: return false;
858: }
859: if (!PaintUtilities.equal(this.domainGridlinePaint,
860: that.domainGridlinePaint)) {
861: return false;
862: }
863: if (!ObjectUtilities.equal(this.domainGridlineStroke,
864: that.domainGridlineStroke)) {
865: return false;
866: }
867: if (!this.rangeGridlinesVisible == that.rangeGridlinesVisible) {
868: return false;
869: }
870: if (!PaintUtilities.equal(this.rangeGridlinePaint,
871: that.rangeGridlinePaint)) {
872: return false;
873: }
874: if (!ObjectUtilities.equal(this.rangeGridlineStroke,
875: that.rangeGridlineStroke)) {
876: return false;
877: }
878: return true;
879: }
880:
881:
889: public Object clone() throws CloneNotSupportedException {
890:
891: FastScatterPlot clone = (FastScatterPlot) super.clone();
892:
893: if (this.data != null) {
894: clone.data = ArrayUtilities.clone(this.data);
895: }
896:
897: if (this.domainAxis != null) {
898: clone.domainAxis = (ValueAxis) this.domainAxis.clone();
899: clone.domainAxis.setPlot(clone);
900: clone.domainAxis.addChangeListener(clone);
901: }
902:
903: if (this.rangeAxis != null) {
904: clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
905: clone.rangeAxis.setPlot(clone);
906: clone.rangeAxis.addChangeListener(clone);
907: }
908:
909: return clone;
910:
911: }
912:
913:
920: private void writeObject(ObjectOutputStream stream) throws IOException {
921: stream.defaultWriteObject();
922: SerialUtilities.writePaint(this.paint, stream);
923: SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
924: SerialUtilities.writePaint(this.domainGridlinePaint, stream);
925: SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
926: SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
927: }
928:
929:
937: private void readObject(ObjectInputStream stream)
938: throws IOException, ClassNotFoundException {
939: stream.defaultReadObject();
940:
941: this.paint = SerialUtilities.readPaint(stream);
942: this.domainGridlineStroke = SerialUtilities.readStroke(stream);
943: this.domainGridlinePaint = SerialUtilities.readPaint(stream);
944:
945: this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
946: this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
947:
948: if (this.domainAxis != null) {
949: this.domainAxis.addChangeListener(this);
950: }
951:
952: if (this.rangeAxis != null) {
953: this.rangeAxis.addChangeListener(this);
954: }
955: }
956:
957: }