1:
80:
81: package ;
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: import ;
97: import ;
98: import ;
99: import ;
100: import ;
101:
102: import ;
103:
104: import ;
105: import ;
106: import ;
107: import ;
108: import ;
109: import ;
110: import ;
111: import ;
112: import ;
113: import ;
114: import ;
115:
116:
121: public abstract class Axis implements Cloneable, Serializable {
122:
123:
124: private static final long serialVersionUID = 7719289504573298271L;
125:
126:
127: public static final boolean DEFAULT_AXIS_VISIBLE = true;
128:
129:
130: public static final Font DEFAULT_AXIS_LABEL_FONT
131: = new Font("SansSerif", Font.PLAIN, 12);
132:
133:
134: public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.black;
135:
136:
137: public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS
138: = new RectangleInsets(3.0, 3.0, 3.0, 3.0);
139:
140:
141: public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.gray;
142:
143:
144: public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(1.0f);
145:
146:
147: public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true;
148:
149:
150: public static final Font DEFAULT_TICK_LABEL_FONT
151: = new Font("SansSerif", Font.PLAIN, 10);
152:
153:
154: public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.black;
155:
156:
157: public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS
158: = new RectangleInsets(2.0, 4.0, 2.0, 4.0);
159:
160:
161: public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true;
162:
163:
164: public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(1);
165:
166:
167: public static final Paint DEFAULT_TICK_MARK_PAINT = Color.gray;
168:
169:
170: public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f;
171:
172:
173: public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f;
174:
175:
176: private boolean visible;
177:
178:
179: private String label;
180:
181:
182: private Font labelFont;
183:
184:
185: private transient Paint labelPaint;
186:
187:
188: private RectangleInsets labelInsets;
189:
190:
191: private double labelAngle;
192:
193:
194: private boolean axisLineVisible;
195:
196:
197: private transient Stroke axisLineStroke;
198:
199:
200: private transient Paint axisLinePaint;
201:
202:
206: private boolean tickLabelsVisible;
207:
208:
209: private Font tickLabelFont;
210:
211:
212: private transient Paint tickLabelPaint;
213:
214:
215: private RectangleInsets tickLabelInsets;
216:
217:
221: private boolean tickMarksVisible;
222:
223:
224: private float tickMarkInsideLength;
225:
226:
227: private float tickMarkOutsideLength;
228:
229:
230: private transient Stroke tickMarkStroke;
231:
232:
233: private transient Paint tickMarkPaint;
234:
235:
236: private double fixedDimension;
237:
238:
242: private transient Plot plot;
243:
244:
245: private transient EventListenerList listenerList;
246:
247:
252: protected Axis(String label) {
253:
254: this.label = label;
255: this.visible = DEFAULT_AXIS_VISIBLE;
256: this.labelFont = DEFAULT_AXIS_LABEL_FONT;
257: this.labelPaint = DEFAULT_AXIS_LABEL_PAINT;
258: this.labelInsets = DEFAULT_AXIS_LABEL_INSETS;
259: this.labelAngle = 0.0;
260:
261: this.axisLineVisible = true;
262: this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT;
263: this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE;
264:
265: this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE;
266: this.tickLabelFont = DEFAULT_TICK_LABEL_FONT;
267: this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT;
268: this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS;
269:
270: this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE;
271: this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE;
272: this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT;
273: this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;
274: this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;
275:
276: this.plot = null;
277:
278: this.listenerList = new EventListenerList();
279:
280: }
281:
282:
290: public boolean isVisible() {
291: return this.visible;
292: }
293:
294:
302: public void setVisible(boolean flag) {
303: if (flag != this.visible) {
304: this.visible = flag;
305: notifyListeners(new AxisChangeEvent(this));
306: }
307: }
308:
309:
318: public String getLabel() {
319: return this.label;
320: }
321:
322:
332: public void setLabel(String label) {
333:
334: String existing = this.label;
335: if (existing != null) {
336: if (!existing.equals(label)) {
337: this.label = label;
338: notifyListeners(new AxisChangeEvent(this));
339: }
340: }
341: else {
342: if (label != null) {
343: this.label = label;
344: notifyListeners(new AxisChangeEvent(this));
345: }
346: }
347:
348: }
349:
350:
357: public Font getLabelFont() {
358: return this.labelFont;
359: }
360:
361:
369: public void setLabelFont(Font font) {
370: if (font == null) {
371: throw new IllegalArgumentException("Null 'font' argument.");
372: }
373: if (!this.labelFont.equals(font)) {
374: this.labelFont = font;
375: notifyListeners(new AxisChangeEvent(this));
376: }
377: }
378:
379:
386: public Paint getLabelPaint() {
387: return this.labelPaint;
388: }
389:
390:
398: public void setLabelPaint(Paint paint) {
399: if (paint == null) {
400: throw new IllegalArgumentException("Null 'paint' argument.");
401: }
402: this.labelPaint = paint;
403: notifyListeners(new AxisChangeEvent(this));
404: }
405:
406:
414: public RectangleInsets getLabelInsets() {
415: return this.labelInsets;
416: }
417:
418:
426: public void setLabelInsets(RectangleInsets insets) {
427: if (insets == null) {
428: throw new IllegalArgumentException("Null 'insets' argument.");
429: }
430: if (!insets.equals(this.labelInsets)) {
431: this.labelInsets = insets;
432: notifyListeners(new AxisChangeEvent(this));
433: }
434: }
435:
436:
443: public double getLabelAngle() {
444: return this.labelAngle;
445: }
446:
447:
455: public void setLabelAngle(double angle) {
456: this.labelAngle = angle;
457: notifyListeners(new AxisChangeEvent(this));
458: }
459:
460:
469: public boolean isAxisLineVisible() {
470: return this.axisLineVisible;
471: }
472:
473:
483: public void setAxisLineVisible(boolean visible) {
484: this.axisLineVisible = visible;
485: notifyListeners(new AxisChangeEvent(this));
486: }
487:
488:
495: public Paint getAxisLinePaint() {
496: return this.axisLinePaint;
497: }
498:
499:
507: public void setAxisLinePaint(Paint paint) {
508: if (paint == null) {
509: throw new IllegalArgumentException("Null 'paint' argument.");
510: }
511: this.axisLinePaint = paint;
512: notifyListeners(new AxisChangeEvent(this));
513: }
514:
515:
522: public Stroke getAxisLineStroke() {
523: return this.axisLineStroke;
524: }
525:
526:
534: public void setAxisLineStroke(Stroke stroke) {
535: if (stroke == null) {
536: throw new IllegalArgumentException("Null 'stroke' argument.");
537: }
538: this.axisLineStroke = stroke;
539: notifyListeners(new AxisChangeEvent(this));
540: }
541:
542:
551: public boolean isTickLabelsVisible() {
552: return this.tickLabelsVisible;
553: }
554:
555:
566: public void setTickLabelsVisible(boolean flag) {
567:
568: if (flag != this.tickLabelsVisible) {
569: this.tickLabelsVisible = flag;
570: notifyListeners(new AxisChangeEvent(this));
571: }
572:
573: }
574:
575:
582: public Font getTickLabelFont() {
583: return this.tickLabelFont;
584: }
585:
586:
594: public void setTickLabelFont(Font font) {
595:
596: if (font == null) {
597: throw new IllegalArgumentException("Null 'font' argument.");
598: }
599:
600: if (!this.tickLabelFont.equals(font)) {
601: this.tickLabelFont = font;
602: notifyListeners(new AxisChangeEvent(this));
603: }
604:
605: }
606:
607:
614: public Paint getTickLabelPaint() {
615: return this.tickLabelPaint;
616: }
617:
618:
626: public void setTickLabelPaint(Paint paint) {
627: if (paint == null) {
628: throw new IllegalArgumentException("Null 'paint' argument.");
629: }
630: this.tickLabelPaint = paint;
631: notifyListeners(new AxisChangeEvent(this));
632: }
633:
634:
641: public RectangleInsets getTickLabelInsets() {
642: return this.tickLabelInsets;
643: }
644:
645:
653: public void setTickLabelInsets(RectangleInsets insets) {
654: if (insets == null) {
655: throw new IllegalArgumentException("Null 'insets' argument.");
656: }
657: if (!this.tickLabelInsets.equals(insets)) {
658: this.tickLabelInsets = insets;
659: notifyListeners(new AxisChangeEvent(this));
660: }
661: }
662:
663:
672: public boolean isTickMarksVisible() {
673: return this.tickMarksVisible;
674: }
675:
676:
684: public void setTickMarksVisible(boolean flag) {
685: if (flag != this.tickMarksVisible) {
686: this.tickMarksVisible = flag;
687: notifyListeners(new AxisChangeEvent(this));
688: }
689: }
690:
691:
699: public float getTickMarkInsideLength() {
700: return this.tickMarkInsideLength;
701: }
702:
703:
711: public void setTickMarkInsideLength(float length) {
712: this.tickMarkInsideLength = length;
713: notifyListeners(new AxisChangeEvent(this));
714: }
715:
716:
724: public float getTickMarkOutsideLength() {
725: return this.tickMarkOutsideLength;
726: }
727:
728:
736: public void setTickMarkOutsideLength(float length) {
737: this.tickMarkOutsideLength = length;
738: notifyListeners(new AxisChangeEvent(this));
739: }
740:
741:
748: public Stroke getTickMarkStroke() {
749: return this.tickMarkStroke;
750: }
751:
752:
760: public void setTickMarkStroke(Stroke stroke) {
761: if (stroke == null) {
762: throw new IllegalArgumentException("Null 'stroke' argument.");
763: }
764: if (!this.tickMarkStroke.equals(stroke)) {
765: this.tickMarkStroke = stroke;
766: notifyListeners(new AxisChangeEvent(this));
767: }
768: }
769:
770:
777: public Paint getTickMarkPaint() {
778: return this.tickMarkPaint;
779: }
780:
781:
789: public void setTickMarkPaint(Paint paint) {
790: if (paint == null) {
791: throw new IllegalArgumentException("Null 'paint' argument.");
792: }
793: this.tickMarkPaint = paint;
794: notifyListeners(new AxisChangeEvent(this));
795: }
796:
797:
806: public Plot getPlot() {
807: return this.plot;
808: }
809:
810:
819: public void setPlot(Plot plot) {
820: this.plot = plot;
821: configure();
822: }
823:
824:
831: public double getFixedDimension() {
832: return this.fixedDimension;
833: }
834:
835:
848: public void setFixedDimension(double dimension) {
849: this.fixedDimension = dimension;
850: }
851:
852:
856: public abstract void configure();
857:
858:
871: public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot,
872: Rectangle2D plotArea,
873: RectangleEdge edge,
874: AxisSpace space);
875:
876:
890: public abstract AxisState draw(Graphics2D g2,
891: double cursor,
892: Rectangle2D plotArea,
893: Rectangle2D dataArea,
894: RectangleEdge edge,
895: PlotRenderingInfo plotState);
896:
897:
908: public abstract List refreshTicks(Graphics2D g2,
909: AxisState state,
910: Rectangle2D dataArea,
911: RectangleEdge edge);
912:
913:
920: public void addChangeListener(AxisChangeListener listener) {
921: this.listenerList.add(AxisChangeListener.class, listener);
922: }
923:
924:
931: public void removeChangeListener(AxisChangeListener listener) {
932: this.listenerList.remove(AxisChangeListener.class, listener);
933: }
934:
935:
944: public boolean hasListener(EventListener listener) {
945: List list = Arrays.asList(this.listenerList.getListenerList());
946: return list.contains(listener);
947: }
948:
949:
955: protected void notifyListeners(AxisChangeEvent event) {
956:
957: Object[] listeners = this.listenerList.getListenerList();
958: for (int i = listeners.length - 2; i >= 0; i -= 2) {
959: if (listeners[i] == AxisChangeListener.class) {
960: ((AxisChangeListener) listeners[i + 1]).axisChanged(event);
961: }
962: }
963:
964: }
965:
966:
975: protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
976:
977: Rectangle2D result = new Rectangle2D.Double();
978: String axisLabel = getLabel();
979: if (axisLabel != null && !axisLabel.equals("")) {
980: FontMetrics fm = g2.getFontMetrics(getLabelFont());
981: Rectangle2D bounds = TextUtilities.getTextBounds(axisLabel, g2, fm);
982: RectangleInsets insets = getLabelInsets();
983: bounds = insets.createOutsetRectangle(bounds);
984: double angle = getLabelAngle();
985: if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) {
986: angle = angle - Math.PI / 2.0;
987: }
988: double x = bounds.getCenterX();
989: double y = bounds.getCenterY();
990: AffineTransform transformer
991: = AffineTransform.getRotateInstance(angle, x, y);
992: Shape labelBounds = transformer.createTransformedShape(bounds);
993: result = labelBounds.getBounds2D();
994: }
995:
996: return result;
997:
998: }
999:
1000:
1012: protected AxisState drawLabel(String label,
1013: Graphics2D g2,
1014: Rectangle2D plotArea,
1015: Rectangle2D dataArea,
1016: RectangleEdge edge,
1017: AxisState state) {
1018:
1019:
1020: if (state == null) {
1021: throw new IllegalArgumentException("Null 'state' argument.");
1022: }
1023:
1024: if ((label == null) || (label.equals(""))) {
1025: return state;
1026: }
1027:
1028: Font font = getLabelFont();
1029: RectangleInsets insets = getLabelInsets();
1030: g2.setFont(font);
1031: g2.setPaint(getLabelPaint());
1032: FontMetrics fm = g2.getFontMetrics();
1033: Rectangle2D labelBounds = TextUtilities.getTextBounds(label, g2, fm);
1034:
1035: if (edge == RectangleEdge.TOP) {
1036:
1037: AffineTransform t = AffineTransform.getRotateInstance(
1038: getLabelAngle(), labelBounds.getCenterX(),
1039: labelBounds.getCenterY());
1040: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1041: labelBounds = rotatedLabelBounds.getBounds2D();
1042: double labelx = dataArea.getCenterX();
1043: double labely = state.getCursor() - insets.getBottom()
1044: - labelBounds.getHeight() / 2.0;
1045: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1046: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1047: TextAnchor.CENTER);
1048: state.cursorUp(insets.getTop() + labelBounds.getHeight()
1049: + insets.getBottom());
1050:
1051: }
1052: else if (edge == RectangleEdge.BOTTOM) {
1053:
1054: AffineTransform t = AffineTransform.getRotateInstance(
1055: getLabelAngle(), labelBounds.getCenterX(),
1056: labelBounds.getCenterY());
1057: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1058: labelBounds = rotatedLabelBounds.getBounds2D();
1059: double labelx = dataArea.getCenterX();
1060: double labely = state.getCursor()
1061: + insets.getTop() + labelBounds.getHeight() / 2.0;
1062: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1063: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1064: TextAnchor.CENTER);
1065: state.cursorDown(insets.getTop() + labelBounds.getHeight()
1066: + insets.getBottom());
1067:
1068: }
1069: else if (edge == RectangleEdge.LEFT) {
1070:
1071: AffineTransform t = AffineTransform.getRotateInstance(
1072: getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(),
1073: labelBounds.getCenterY());
1074: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1075: labelBounds = rotatedLabelBounds.getBounds2D();
1076: double labelx = state.getCursor()
1077: - insets.getRight() - labelBounds.getWidth() / 2.0;
1078: double labely = dataArea.getCenterY();
1079: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1080: (float) labely, TextAnchor.CENTER,
1081: getLabelAngle() - Math.PI / 2.0, TextAnchor.CENTER);
1082: state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
1083: + insets.getRight());
1084: }
1085: else if (edge == RectangleEdge.RIGHT) {
1086:
1087: AffineTransform t = AffineTransform.getRotateInstance(
1088: getLabelAngle() + Math.PI / 2.0,
1089: labelBounds.getCenterX(), labelBounds.getCenterY());
1090: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1091: labelBounds = rotatedLabelBounds.getBounds2D();
1092: double labelx = state.getCursor()
1093: + insets.getLeft() + labelBounds.getWidth() / 2.0;
1094: double labely = dataArea.getY() + dataArea.getHeight() / 2.0;
1095: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1096: (float) labely, TextAnchor.CENTER,
1097: getLabelAngle() + Math.PI / 2.0, TextAnchor.CENTER);
1098: state.cursorRight(insets.getLeft() + labelBounds.getWidth()
1099: + insets.getRight());
1100:
1101: }
1102:
1103: return state;
1104:
1105: }
1106:
1107:
1115: protected void drawAxisLine(Graphics2D g2, double cursor,
1116: Rectangle2D dataArea, RectangleEdge edge) {
1117:
1118: Line2D axisLine = null;
1119: if (edge == RectangleEdge.TOP) {
1120: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1121: dataArea.getMaxX(), cursor);
1122: }
1123: else if (edge == RectangleEdge.BOTTOM) {
1124: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1125: dataArea.getMaxX(), cursor);
1126: }
1127: else if (edge == RectangleEdge.LEFT) {
1128: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1129: dataArea.getMaxY());
1130: }
1131: else if (edge == RectangleEdge.RIGHT) {
1132: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1133: dataArea.getMaxY());
1134: }
1135: g2.setPaint(this.axisLinePaint);
1136: g2.setStroke(this.axisLineStroke);
1137: g2.draw(axisLine);
1138:
1139: }
1140:
1141:
1149: public Object clone() throws CloneNotSupportedException {
1150: Axis clone = (Axis) super.clone();
1151:
1152: clone.plot = null;
1153: clone.listenerList = new EventListenerList();
1154: return clone;
1155: }
1156:
1157:
1164: public boolean equals(Object obj) {
1165: if (obj == this) {
1166: return true;
1167: }
1168: if (!(obj instanceof Axis)) {
1169: return false;
1170: }
1171: Axis that = (Axis) obj;
1172: if (this.visible != that.visible) {
1173: return false;
1174: }
1175: if (!ObjectUtilities.equal(this.label, that.label)) {
1176: return false;
1177: }
1178: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
1179: return false;
1180: }
1181: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
1182: return false;
1183: }
1184: if (!ObjectUtilities.equal(this.labelInsets, that.labelInsets)) {
1185: return false;
1186: }
1187: if (this.labelAngle != that.labelAngle) {
1188: return false;
1189: }
1190: if (this.axisLineVisible != that.axisLineVisible) {
1191: return false;
1192: }
1193: if (!ObjectUtilities.equal(this.axisLineStroke, that.axisLineStroke)) {
1194: return false;
1195: }
1196: if (!PaintUtilities.equal(this.axisLinePaint, that.axisLinePaint)) {
1197: return false;
1198: }
1199: if (this.tickLabelsVisible != that.tickLabelsVisible) {
1200: return false;
1201: }
1202: if (!ObjectUtilities.equal(this.tickLabelFont, that.tickLabelFont)) {
1203: return false;
1204: }
1205: if (!PaintUtilities.equal(this.tickLabelPaint, that.tickLabelPaint)) {
1206: return false;
1207: }
1208: if (!ObjectUtilities.equal(
1209: this.tickLabelInsets, that.tickLabelInsets
1210: )) {
1211: return false;
1212: }
1213: if (this.tickMarksVisible != that.tickMarksVisible) {
1214: return false;
1215: }
1216: if (this.tickMarkInsideLength != that.tickMarkInsideLength) {
1217: return false;
1218: }
1219: if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) {
1220: return false;
1221: }
1222: if (!PaintUtilities.equal(this.tickMarkPaint, that.tickMarkPaint)) {
1223: return false;
1224: }
1225: if (!ObjectUtilities.equal(this.tickMarkStroke, that.tickMarkStroke)) {
1226: return false;
1227: }
1228: if (this.fixedDimension != that.fixedDimension) {
1229: return false;
1230: }
1231: return true;
1232: }
1233:
1234:
1241: private void writeObject(ObjectOutputStream stream) throws IOException {
1242: stream.defaultWriteObject();
1243: SerialUtilities.writePaint(this.labelPaint, stream);
1244: SerialUtilities.writePaint(this.tickLabelPaint, stream);
1245: SerialUtilities.writeStroke(this.axisLineStroke, stream);
1246: SerialUtilities.writePaint(this.axisLinePaint, stream);
1247: SerialUtilities.writeStroke(this.tickMarkStroke, stream);
1248: SerialUtilities.writePaint(this.tickMarkPaint, stream);
1249: }
1250:
1251:
1259: private void readObject(ObjectInputStream stream)
1260: throws IOException, ClassNotFoundException {
1261: stream.defaultReadObject();
1262: this.labelPaint = SerialUtilities.readPaint(stream);
1263: this.tickLabelPaint = SerialUtilities.readPaint(stream);
1264: this.axisLineStroke = SerialUtilities.readStroke(stream);
1265: this.axisLinePaint = SerialUtilities.readPaint(stream);
1266: this.tickMarkStroke = SerialUtilities.readStroke(stream);
1267: this.tickMarkPaint = SerialUtilities.readPaint(stream);
1268: this.listenerList = new EventListenerList();
1269: }
1270:
1271: }