1:
60:
61: package ;
62:
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: import ;
80: import ;
81:
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: import ;
95:
96:
99: public class FastScatterPlot extends Plot implements ValueAxisPlot,
100: Zoomable, Cloneable, Serializable {
101:
102:
103: private static final long serialVersionUID = 7871545897358563521L;
104:
105:
106: public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
107: BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[]
108: {2.0f, 2.0f}, 0.0f);
109:
110:
111: public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
112:
113:
114: private float[][] data;
115:
116:
117: private Range xDataRange;
118:
119:
120: private Range yDataRange;
121:
122:
123: private ValueAxis domainAxis;
124:
125:
126: private ValueAxis rangeAxis;
127:
128:
129: private transient Paint paint;
130:
131:
132: private boolean domainGridlinesVisible;
133:
134:
135: private transient Stroke domainGridlineStroke;
136:
137:
138: private transient Paint domainGridlinePaint;
139:
140:
141: private boolean rangeGridlinesVisible;
142:
143:
144: private transient Stroke rangeGridlineStroke;
145:
146:
147: private transient Paint rangeGridlinePaint;
148:
149:
150: protected static ResourceBundle localizationResources =
151: ResourceBundle.getBundle(
152: "org.jfree.chart.plot.LocalizationBundle");
153:
154:
158: public FastScatterPlot() {
159: this(null, new NumberAxis("X"), new NumberAxis("Y"));
160: }
161:
162:
171: public FastScatterPlot(float[][] data,
172: ValueAxis domainAxis, ValueAxis rangeAxis) {
173:
174: super();
175: if (domainAxis == null) {
176: throw new IllegalArgumentException("Null 'domainAxis' argument.");
177: }
178: if (rangeAxis == null) {
179: throw new IllegalArgumentException("Null 'rangeAxis' argument.");
180: }
181:
182: this.data = data;
183: this.xDataRange = calculateXDataRange(data);
184: this.yDataRange = calculateYDataRange(data);
185: this.domainAxis = domainAxis;
186: this.domainAxis.setPlot(this);
187: this.domainAxis.addChangeListener(this);
188: this.rangeAxis = rangeAxis;
189: this.rangeAxis.setPlot(this);
190: this.rangeAxis.addChangeListener(this);
191:
192: this.paint = Color.red;
193:
194: this.domainGridlinesVisible = true;
195: this.domainGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
196: this.domainGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
197:
198: this.rangeGridlinesVisible = true;
199: this.rangeGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT;
200: this.rangeGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE;
201:
202: }
203:
204:
209: public String getPlotType() {
210: return localizationResources.getString("Fast_Scatter_Plot");
211: }
212:
213:
220: public float[][] getData() {
221: return this.data;
222: }
223:
224:
232: public void setData(float[][] data) {
233: this.data = data;
234: notifyListeners(new PlotChangeEvent(this));
235: }
236:
237:
242: public PlotOrientation getOrientation() {
243: return PlotOrientation.VERTICAL;
244: }
245:
246:
253: public ValueAxis getDomainAxis() {
254: return this.domainAxis;
255: }
256:
257:
267: public void setDomainAxis(ValueAxis axis) {
268: if (axis == null) {
269: throw new IllegalArgumentException("Null 'axis' argument.");
270: }
271: this.domainAxis = axis;
272: notifyListeners(new PlotChangeEvent(this));
273: }
274:
275:
282: public ValueAxis getRangeAxis() {
283: return this.rangeAxis;
284: }
285:
286:
296: public void setRangeAxis(ValueAxis axis) {
297: if (axis == null) {
298: throw new IllegalArgumentException("Null 'axis' argument.");
299: }
300: this.rangeAxis = axis;
301: notifyListeners(new PlotChangeEvent(this));
302: }
303:
304:
312: public Paint getPaint() {
313: return this.paint;
314: }
315:
316:
324: public void setPaint(Paint paint) {
325: if (paint == null) {
326: throw new IllegalArgumentException("Null 'paint' argument.");
327: }
328: this.paint = paint;
329: notifyListeners(new PlotChangeEvent(this));
330: }
331:
332:
341: public boolean isDomainGridlinesVisible() {
342: return this.domainGridlinesVisible;
343: }
344:
345:
354: public void setDomainGridlinesVisible(boolean visible) {
355: if (this.domainGridlinesVisible != visible) {
356: this.domainGridlinesVisible = visible;
357: notifyListeners(new PlotChangeEvent(this));
358: }
359: }
360:
361:
369: public Stroke getDomainGridlineStroke() {
370: return this.domainGridlineStroke;
371: }
372:
373:
381: public void setDomainGridlineStroke(Stroke stroke) {
382: if (stroke == null) {
383: throw new IllegalArgumentException("Null 'stroke' argument.");
384: }
385: this.domainGridlineStroke = stroke;
386: notifyListeners(new PlotChangeEvent(this));
387: }
388:
389:
397: public Paint getDomainGridlinePaint() {
398: return this.domainGridlinePaint;
399: }
400:
401:
409: public void setDomainGridlinePaint(Paint paint) {
410: if (paint == null) {
411: throw new IllegalArgumentException("Null 'paint' argument.");
412: }
413: this.domainGridlinePaint = paint;
414: notifyListeners(new PlotChangeEvent(this));
415: }
416:
417:
425: public boolean isRangeGridlinesVisible() {
426: return this.rangeGridlinesVisible;
427: }
428:
429:
438: public void setRangeGridlinesVisible(boolean visible) {
439: if (this.rangeGridlinesVisible != visible) {
440: this.rangeGridlinesVisible = visible;
441: notifyListeners(new PlotChangeEvent(this));
442: }
443: }
444:
445:
453: public Stroke getRangeGridlineStroke() {
454: return this.rangeGridlineStroke;
455: }
456:
457:
465: public void setRangeGridlineStroke(Stroke stroke) {
466: if (stroke == null) {
467: throw new IllegalArgumentException("Null 'stroke' argument.");
468: }
469: this.rangeGridlineStroke = stroke;
470: notifyListeners(new PlotChangeEvent(this));
471: }
472:
473:
481: public Paint getRangeGridlinePaint() {
482: return this.rangeGridlinePaint;
483: }
484:
485:
493: public void setRangeGridlinePaint(Paint paint) {
494: if (paint == null) {
495: throw new IllegalArgumentException("Null 'paint' argument.");
496: }
497: this.rangeGridlinePaint = paint;
498: notifyListeners(new PlotChangeEvent(this));
499: }
500:
501:
513: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
514: PlotState parentState,
515: PlotRenderingInfo info) {
516:
517:
518: if (info != null) {
519: info.setPlotArea(area);
520: }
521:
522:
523: RectangleInsets insets = getInsets();
524: insets.trim(area);
525:
526: AxisSpace space = new AxisSpace();
527: space = this.domainAxis.reserveSpace(g2, this, area,
528: RectangleEdge.BOTTOM, space);
529: space = this.rangeAxis.reserveSpace(g2, this, area, RectangleEdge.LEFT,
530: space);
531: Rectangle2D dataArea = space.shrink(area, null);
532:
533: if (info != null) {
534: info.setDataArea(dataArea);
535: }
536:
537:
538: drawBackground(g2, dataArea);
539:
540: AxisState domainAxisState = this.domainAxis.draw(g2,
541: dataArea.getMaxY(), area, dataArea, RectangleEdge.BOTTOM, info);
542: AxisState rangeAxisState = this.rangeAxis.draw(g2, dataArea.getMinX(),
543: area, dataArea, RectangleEdge.LEFT, info);
544: drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
545: drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
546:
547: Shape originalClip = g2.getClip();
548: Composite originalComposite = g2.getComposite();
549:
550: g2.clip(dataArea);
551: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
552: getForegroundAlpha()));
553:
554: render(g2, dataArea, info, null);
555:
556: g2.setClip(originalClip);
557: g2.setComposite(originalComposite);
558: drawOutline(g2, dataArea);
559:
560: }
561:
562:
573: public void render(Graphics2D g2, Rectangle2D dataArea,
574: PlotRenderingInfo info, CrosshairState crosshairState) {
575:
576:
577:
578:
579: g2.setPaint(this.paint);
580:
581:
582:
583:
584:
585:
586:
587:
588:
589:
590:
591:
592:
593:
594: if (this.data != null) {
595: for (int i = 0; i < this.data[0].length; i++) {
596: float x = this.data[0][i];
597: float y = this.data[1][i];
598:
599:
600:
601: int transX = (int) this.domainAxis.valueToJava2D(x, dataArea,
602: RectangleEdge.BOTTOM);
603: int transY = (int) this.rangeAxis.valueToJava2D(y, dataArea,
604: RectangleEdge.LEFT);
605: g2.fillRect(transX, transY, 1, 1);
606: }
607: }
608:
609:
610:
611:
612: }
613:
614:
621: protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea,
622: List ticks) {
623:
624:
625: if (isDomainGridlinesVisible()) {
626: Iterator iterator = ticks.iterator();
627: while (iterator.hasNext()) {
628: ValueTick tick = (ValueTick) iterator.next();
629: double v = this.domainAxis.valueToJava2D(tick.getValue(),
630: dataArea, RectangleEdge.BOTTOM);
631: Line2D line = new Line2D.Double(v, dataArea.getMinY(), v,
632: dataArea.getMaxY());
633: g2.setPaint(getDomainGridlinePaint());
634: g2.setStroke(getDomainGridlineStroke());
635: g2.draw(line);
636: }
637: }
638: }
639:
640:
647: protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea,
648: List ticks) {
649:
650:
651: if (isRangeGridlinesVisible()) {
652: Iterator iterator = ticks.iterator();
653: while (iterator.hasNext()) {
654: ValueTick tick = (ValueTick) iterator.next();
655: double v = this.rangeAxis.valueToJava2D(tick.getValue(),
656: dataArea, RectangleEdge.LEFT);
657: Line2D line = new Line2D.Double(dataArea.getMinX(), v,
658: dataArea.getMaxX(), v);
659: g2.setPaint(getRangeGridlinePaint());
660: g2.setStroke(getRangeGridlineStroke());
661: g2.draw(line);
662: }
663: }
664:
665: }
666:
667:
676: public Range getDataRange(ValueAxis axis) {
677: Range result = null;
678: if (axis == this.domainAxis) {
679: result = this.xDataRange;
680: }
681: else if (axis == this.rangeAxis) {
682: result = this.yDataRange;
683: }
684: return result;
685: }
686:
687:
694: private Range calculateXDataRange(float[][] data) {
695:
696: Range result = null;
697:
698: if (data != null) {
699: float lowest = Float.POSITIVE_INFINITY;
700: float highest = Float.NEGATIVE_INFINITY;
701: for (int i = 0; i < data[0].length; i++) {
702: float v = data[0][i];
703: if (v < lowest) {
704: lowest = v;
705: }
706: if (v > highest) {
707: highest = v;
708: }
709: }
710: if (lowest <= highest) {
711: result = new Range(lowest, highest);
712: }
713: }
714:
715: return result;
716:
717: }
718:
719:
726: private Range calculateYDataRange(float[][] data) {
727:
728: Range result = null;
729:
730: if (data != null) {
731: float lowest = Float.POSITIVE_INFINITY;
732: float highest = Float.NEGATIVE_INFINITY;
733: for (int i = 0; i < data[0].length; i++) {
734: float v = data[1][i];
735: if (v < lowest) {
736: lowest = v;
737: }
738: if (v > highest) {
739: highest = v;
740: }
741: }
742: if (lowest <= highest) {
743: result = new Range(lowest, highest);
744: }
745: }
746: return result;
747:
748: }
749:
750:
757: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
758: Point2D source) {
759: this.domainAxis.resizeRange(factor);
760: }
761:
762:
774: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
775: Point2D source, boolean useAnchor) {
776:
777: if (useAnchor) {
778:
779:
780: double sourceX = source.getX();
781: double anchorX = this.domainAxis.java2DToValue(sourceX,
782: info.getDataArea(), RectangleEdge.BOTTOM);
783: this.domainAxis.resizeRange(factor, anchorX);
784: }
785: else {
786: this.domainAxis.resizeRange(factor);
787: }
788:
789: }
790:
791:
801: public void zoomDomainAxes(double lowerPercent, double upperPercent,
802: PlotRenderingInfo info, Point2D source) {
803: this.domainAxis.zoomRange(lowerPercent, upperPercent);
804: }
805:
806:
813: public void zoomRangeAxes(double factor,
814: PlotRenderingInfo info, Point2D source) {
815: this.rangeAxis.resizeRange(factor);
816: }
817:
818:
830: public void zoomRangeAxes(double factor, PlotRenderingInfo info,
831: Point2D source, boolean useAnchor) {
832:
833: if (useAnchor) {
834:
835:
836: double sourceX = source.getX();
837: double anchorX = this.rangeAxis.java2DToValue(sourceX,
838: info.getDataArea(), RectangleEdge.LEFT);
839: this.rangeAxis.resizeRange(factor, anchorX);
840: }
841: else {
842: this.rangeAxis.resizeRange(factor);
843: }
844:
845: }
846:
847:
857: public void zoomRangeAxes(double lowerPercent, double upperPercent,
858: PlotRenderingInfo info, Point2D source) {
859: this.rangeAxis.zoomRange(lowerPercent, upperPercent);
860: }
861:
862:
867: public boolean isDomainZoomable() {
868: return true;
869: }
870:
871:
876: public boolean isRangeZoomable() {
877: return true;
878: }
879:
880:
887: public boolean equals(Object obj) {
888: if (obj == this) {
889: return true;
890: }
891: if (!super.equals(obj)) {
892: return false;
893: }
894: if (!(obj instanceof FastScatterPlot)) {
895: return false;
896: }
897: FastScatterPlot that = (FastScatterPlot) obj;
898: if (!ArrayUtilities.equal(this.data, that.data)) {
899: return false;
900: }
901: if (!ObjectUtilities.equal(this.domainAxis, that.domainAxis)) {
902: return false;
903: }
904: if (!ObjectUtilities.equal(this.rangeAxis, that.rangeAxis)) {
905: return false;
906: }
907: if (!PaintUtilities.equal(this.paint, that.paint)) {
908: return false;
909: }
910: if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
911: return false;
912: }
913: if (!PaintUtilities.equal(this.domainGridlinePaint,
914: that.domainGridlinePaint)) {
915: return false;
916: }
917: if (!ObjectUtilities.equal(this.domainGridlineStroke,
918: that.domainGridlineStroke)) {
919: return false;
920: }
921: if (!this.rangeGridlinesVisible == that.rangeGridlinesVisible) {
922: return false;
923: }
924: if (!PaintUtilities.equal(this.rangeGridlinePaint,
925: that.rangeGridlinePaint)) {
926: return false;
927: }
928: if (!ObjectUtilities.equal(this.rangeGridlineStroke,
929: that.rangeGridlineStroke)) {
930: return false;
931: }
932: return true;
933: }
934:
935:
943: public Object clone() throws CloneNotSupportedException {
944:
945: FastScatterPlot clone = (FastScatterPlot) super.clone();
946:
947: if (this.data != null) {
948: clone.data = ArrayUtilities.clone(this.data);
949: }
950:
951: if (this.domainAxis != null) {
952: clone.domainAxis = (ValueAxis) this.domainAxis.clone();
953: clone.domainAxis.setPlot(clone);
954: clone.domainAxis.addChangeListener(clone);
955: }
956:
957: if (this.rangeAxis != null) {
958: clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
959: clone.rangeAxis.setPlot(clone);
960: clone.rangeAxis.addChangeListener(clone);
961: }
962:
963: return clone;
964:
965: }
966:
967:
974: private void writeObject(ObjectOutputStream stream) throws IOException {
975: stream.defaultWriteObject();
976: SerialUtilities.writePaint(this.paint, stream);
977: SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
978: SerialUtilities.writePaint(this.domainGridlinePaint, stream);
979: SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
980: SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
981: }
982:
983:
991: private void readObject(ObjectInputStream stream)
992: throws IOException, ClassNotFoundException {
993: stream.defaultReadObject();
994:
995: this.paint = SerialUtilities.readPaint(stream);
996: this.domainGridlineStroke = SerialUtilities.readStroke(stream);
997: this.domainGridlinePaint = SerialUtilities.readPaint(stream);
998:
999: this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
1000: this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
1001:
1002: if (this.domainAxis != null) {
1003: this.domainAxis.addChangeListener(this);
1004: }
1005:
1006: if (this.rangeAxis != null) {
1007: this.rangeAxis.addChangeListener(this);
1008: }
1009: }
1010:
1011: }