1:
81:
82: package ;
83:
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: import ;
102:
103: import ;
104:
105: import ;
106: import ;
107: import ;
108: import ;
109: import ;
110: import ;
111: import ;
112: import ;
113: import ;
114: import ;
115: import ;
116:
117:
122: public abstract class Axis implements Cloneable, Serializable {
123:
124:
125: private static final long serialVersionUID = 7719289504573298271L;
126:
127:
128: public static final boolean DEFAULT_AXIS_VISIBLE = true;
129:
130:
131: public static final Font DEFAULT_AXIS_LABEL_FONT
132: = new Font("SansSerif", Font.PLAIN, 12);
133:
134:
135: public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.black;
136:
137:
138: public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS
139: = new RectangleInsets(3.0, 3.0, 3.0, 3.0);
140:
141:
142: public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.gray;
143:
144:
145: public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(1.0f);
146:
147:
148: public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true;
149:
150:
151: public static final Font DEFAULT_TICK_LABEL_FONT
152: = new Font("SansSerif", Font.PLAIN, 10);
153:
154:
155: public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.black;
156:
157:
158: public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS
159: = new RectangleInsets(2.0, 4.0, 2.0, 4.0);
160:
161:
162: public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true;
163:
164:
165: public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(1);
166:
167:
168: public static final Paint DEFAULT_TICK_MARK_PAINT = Color.gray;
169:
170:
171: public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f;
172:
173:
174: public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f;
175:
176:
177: private boolean visible;
178:
179:
180: private String label;
181:
182:
183: private Font labelFont;
184:
185:
186: private transient Paint labelPaint;
187:
188:
189: private RectangleInsets labelInsets;
190:
191:
192: private double labelAngle;
193:
194:
195: private boolean axisLineVisible;
196:
197:
198: private transient Stroke axisLineStroke;
199:
200:
201: private transient Paint axisLinePaint;
202:
203:
207: private boolean tickLabelsVisible;
208:
209:
210: private Font tickLabelFont;
211:
212:
213: private transient Paint tickLabelPaint;
214:
215:
216: private RectangleInsets tickLabelInsets;
217:
218:
222: private boolean tickMarksVisible;
223:
224:
225: private float tickMarkInsideLength;
226:
227:
228: private float tickMarkOutsideLength;
229:
230:
231: private transient Stroke tickMarkStroke;
232:
233:
234: private transient Paint tickMarkPaint;
235:
236:
237: private double fixedDimension;
238:
239:
243: private transient Plot plot;
244:
245:
246: private transient EventListenerList listenerList;
247:
248:
253: protected Axis(String label) {
254:
255: this.label = label;
256: this.visible = DEFAULT_AXIS_VISIBLE;
257: this.labelFont = DEFAULT_AXIS_LABEL_FONT;
258: this.labelPaint = DEFAULT_AXIS_LABEL_PAINT;
259: this.labelInsets = DEFAULT_AXIS_LABEL_INSETS;
260: this.labelAngle = 0.0;
261:
262: this.axisLineVisible = true;
263: this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT;
264: this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE;
265:
266: this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE;
267: this.tickLabelFont = DEFAULT_TICK_LABEL_FONT;
268: this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT;
269: this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS;
270:
271: this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE;
272: this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE;
273: this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT;
274: this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;
275: this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;
276:
277: this.plot = null;
278:
279: this.listenerList = new EventListenerList();
280:
281: }
282:
283:
291: public boolean isVisible() {
292: return this.visible;
293: }
294:
295:
303: public void setVisible(boolean flag) {
304: if (flag != this.visible) {
305: this.visible = flag;
306: notifyListeners(new AxisChangeEvent(this));
307: }
308: }
309:
310:
319: public String getLabel() {
320: return this.label;
321: }
322:
323:
333: public void setLabel(String label) {
334:
335: String existing = this.label;
336: if (existing != null) {
337: if (!existing.equals(label)) {
338: this.label = label;
339: notifyListeners(new AxisChangeEvent(this));
340: }
341: }
342: else {
343: if (label != null) {
344: this.label = label;
345: notifyListeners(new AxisChangeEvent(this));
346: }
347: }
348:
349: }
350:
351:
358: public Font getLabelFont() {
359: return this.labelFont;
360: }
361:
362:
370: public void setLabelFont(Font font) {
371: if (font == null) {
372: throw new IllegalArgumentException("Null 'font' argument.");
373: }
374: if (!this.labelFont.equals(font)) {
375: this.labelFont = font;
376: notifyListeners(new AxisChangeEvent(this));
377: }
378: }
379:
380:
387: public Paint getLabelPaint() {
388: return this.labelPaint;
389: }
390:
391:
399: public void setLabelPaint(Paint paint) {
400: if (paint == null) {
401: throw new IllegalArgumentException("Null 'paint' argument.");
402: }
403: this.labelPaint = paint;
404: notifyListeners(new AxisChangeEvent(this));
405: }
406:
407:
415: public RectangleInsets getLabelInsets() {
416: return this.labelInsets;
417: }
418:
419:
427: public void setLabelInsets(RectangleInsets insets) {
428: setLabelInsets(insets, true);
429: }
430:
431:
440: public void setLabelInsets(RectangleInsets insets, boolean notify) {
441: if (insets == null) {
442: throw new IllegalArgumentException("Null 'insets' argument.");
443: }
444: if (!insets.equals(this.labelInsets)) {
445: this.labelInsets = insets;
446: if (notify) {
447: notifyListeners(new AxisChangeEvent(this));
448: }
449: }
450: }
451:
452:
459: public double getLabelAngle() {
460: return this.labelAngle;
461: }
462:
463:
471: public void setLabelAngle(double angle) {
472: this.labelAngle = angle;
473: notifyListeners(new AxisChangeEvent(this));
474: }
475:
476:
485: public boolean isAxisLineVisible() {
486: return this.axisLineVisible;
487: }
488:
489:
499: public void setAxisLineVisible(boolean visible) {
500: this.axisLineVisible = visible;
501: notifyListeners(new AxisChangeEvent(this));
502: }
503:
504:
511: public Paint getAxisLinePaint() {
512: return this.axisLinePaint;
513: }
514:
515:
523: public void setAxisLinePaint(Paint paint) {
524: if (paint == null) {
525: throw new IllegalArgumentException("Null 'paint' argument.");
526: }
527: this.axisLinePaint = paint;
528: notifyListeners(new AxisChangeEvent(this));
529: }
530:
531:
538: public Stroke getAxisLineStroke() {
539: return this.axisLineStroke;
540: }
541:
542:
550: public void setAxisLineStroke(Stroke stroke) {
551: if (stroke == null) {
552: throw new IllegalArgumentException("Null 'stroke' argument.");
553: }
554: this.axisLineStroke = stroke;
555: notifyListeners(new AxisChangeEvent(this));
556: }
557:
558:
567: public boolean isTickLabelsVisible() {
568: return this.tickLabelsVisible;
569: }
570:
571:
582: public void setTickLabelsVisible(boolean flag) {
583:
584: if (flag != this.tickLabelsVisible) {
585: this.tickLabelsVisible = flag;
586: notifyListeners(new AxisChangeEvent(this));
587: }
588:
589: }
590:
591:
598: public Font getTickLabelFont() {
599: return this.tickLabelFont;
600: }
601:
602:
610: public void setTickLabelFont(Font font) {
611:
612: if (font == null) {
613: throw new IllegalArgumentException("Null 'font' argument.");
614: }
615:
616: if (!this.tickLabelFont.equals(font)) {
617: this.tickLabelFont = font;
618: notifyListeners(new AxisChangeEvent(this));
619: }
620:
621: }
622:
623:
630: public Paint getTickLabelPaint() {
631: return this.tickLabelPaint;
632: }
633:
634:
642: public void setTickLabelPaint(Paint paint) {
643: if (paint == null) {
644: throw new IllegalArgumentException("Null 'paint' argument.");
645: }
646: this.tickLabelPaint = paint;
647: notifyListeners(new AxisChangeEvent(this));
648: }
649:
650:
657: public RectangleInsets getTickLabelInsets() {
658: return this.tickLabelInsets;
659: }
660:
661:
669: public void setTickLabelInsets(RectangleInsets insets) {
670: if (insets == null) {
671: throw new IllegalArgumentException("Null 'insets' argument.");
672: }
673: if (!this.tickLabelInsets.equals(insets)) {
674: this.tickLabelInsets = insets;
675: notifyListeners(new AxisChangeEvent(this));
676: }
677: }
678:
679:
688: public boolean isTickMarksVisible() {
689: return this.tickMarksVisible;
690: }
691:
692:
700: public void setTickMarksVisible(boolean flag) {
701: if (flag != this.tickMarksVisible) {
702: this.tickMarksVisible = flag;
703: notifyListeners(new AxisChangeEvent(this));
704: }
705: }
706:
707:
715: public float getTickMarkInsideLength() {
716: return this.tickMarkInsideLength;
717: }
718:
719:
727: public void setTickMarkInsideLength(float length) {
728: this.tickMarkInsideLength = length;
729: notifyListeners(new AxisChangeEvent(this));
730: }
731:
732:
740: public float getTickMarkOutsideLength() {
741: return this.tickMarkOutsideLength;
742: }
743:
744:
752: public void setTickMarkOutsideLength(float length) {
753: this.tickMarkOutsideLength = length;
754: notifyListeners(new AxisChangeEvent(this));
755: }
756:
757:
764: public Stroke getTickMarkStroke() {
765: return this.tickMarkStroke;
766: }
767:
768:
776: public void setTickMarkStroke(Stroke stroke) {
777: if (stroke == null) {
778: throw new IllegalArgumentException("Null 'stroke' argument.");
779: }
780: if (!this.tickMarkStroke.equals(stroke)) {
781: this.tickMarkStroke = stroke;
782: notifyListeners(new AxisChangeEvent(this));
783: }
784: }
785:
786:
793: public Paint getTickMarkPaint() {
794: return this.tickMarkPaint;
795: }
796:
797:
805: public void setTickMarkPaint(Paint paint) {
806: if (paint == null) {
807: throw new IllegalArgumentException("Null 'paint' argument.");
808: }
809: this.tickMarkPaint = paint;
810: notifyListeners(new AxisChangeEvent(this));
811: }
812:
813:
822: public Plot getPlot() {
823: return this.plot;
824: }
825:
826:
835: public void setPlot(Plot plot) {
836: this.plot = plot;
837: configure();
838: }
839:
840:
847: public double getFixedDimension() {
848: return this.fixedDimension;
849: }
850:
851:
864: public void setFixedDimension(double dimension) {
865: this.fixedDimension = dimension;
866: }
867:
868:
872: public abstract void configure();
873:
874:
887: public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot,
888: Rectangle2D plotArea,
889: RectangleEdge edge,
890: AxisSpace space);
891:
892:
906: public abstract AxisState draw(Graphics2D g2,
907: double cursor,
908: Rectangle2D plotArea,
909: Rectangle2D dataArea,
910: RectangleEdge edge,
911: PlotRenderingInfo plotState);
912:
913:
924: public abstract List refreshTicks(Graphics2D g2,
925: AxisState state,
926: Rectangle2D dataArea,
927: RectangleEdge edge);
928:
929:
936: public void addChangeListener(AxisChangeListener listener) {
937: this.listenerList.add(AxisChangeListener.class, listener);
938: }
939:
940:
947: public void removeChangeListener(AxisChangeListener listener) {
948: this.listenerList.remove(AxisChangeListener.class, listener);
949: }
950:
951:
960: public boolean hasListener(EventListener listener) {
961: List list = Arrays.asList(this.listenerList.getListenerList());
962: return list.contains(listener);
963: }
964:
965:
971: protected void notifyListeners(AxisChangeEvent event) {
972:
973: Object[] listeners = this.listenerList.getListenerList();
974: for (int i = listeners.length - 2; i >= 0; i -= 2) {
975: if (listeners[i] == AxisChangeListener.class) {
976: ((AxisChangeListener) listeners[i + 1]).axisChanged(event);
977: }
978: }
979:
980: }
981:
982:
991: protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
992:
993: Rectangle2D result = new Rectangle2D.Double();
994: String axisLabel = getLabel();
995: if (axisLabel != null && !axisLabel.equals("")) {
996: FontMetrics fm = g2.getFontMetrics(getLabelFont());
997: Rectangle2D bounds = TextUtilities.getTextBounds(axisLabel, g2, fm);
998: RectangleInsets insets = getLabelInsets();
999: bounds = insets.createOutsetRectangle(bounds);
1000: double angle = getLabelAngle();
1001: if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) {
1002: angle = angle - Math.PI / 2.0;
1003: }
1004: double x = bounds.getCenterX();
1005: double y = bounds.getCenterY();
1006: AffineTransform transformer
1007: = AffineTransform.getRotateInstance(angle, x, y);
1008: Shape labelBounds = transformer.createTransformedShape(bounds);
1009: result = labelBounds.getBounds2D();
1010: }
1011:
1012: return result;
1013:
1014: }
1015:
1016:
1028: protected AxisState drawLabel(String label,
1029: Graphics2D g2,
1030: Rectangle2D plotArea,
1031: Rectangle2D dataArea,
1032: RectangleEdge edge,
1033: AxisState state) {
1034:
1035:
1036: if (state == null) {
1037: throw new IllegalArgumentException("Null 'state' argument.");
1038: }
1039:
1040: if ((label == null) || (label.equals(""))) {
1041: return state;
1042: }
1043:
1044: Font font = getLabelFont();
1045: RectangleInsets insets = getLabelInsets();
1046: g2.setFont(font);
1047: g2.setPaint(getLabelPaint());
1048: FontMetrics fm = g2.getFontMetrics();
1049: Rectangle2D labelBounds = TextUtilities.getTextBounds(label, g2, fm);
1050:
1051: if (edge == RectangleEdge.TOP) {
1052:
1053: AffineTransform t = AffineTransform.getRotateInstance(
1054: getLabelAngle(), labelBounds.getCenterX(),
1055: labelBounds.getCenterY());
1056: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1057: labelBounds = rotatedLabelBounds.getBounds2D();
1058: double labelx = dataArea.getCenterX();
1059: double labely = state.getCursor() - insets.getBottom()
1060: - labelBounds.getHeight() / 2.0;
1061: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1062: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1063: TextAnchor.CENTER);
1064: state.cursorUp(insets.getTop() + labelBounds.getHeight()
1065: + insets.getBottom());
1066:
1067: }
1068: else if (edge == RectangleEdge.BOTTOM) {
1069:
1070: AffineTransform t = AffineTransform.getRotateInstance(
1071: getLabelAngle(), labelBounds.getCenterX(),
1072: labelBounds.getCenterY());
1073: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1074: labelBounds = rotatedLabelBounds.getBounds2D();
1075: double labelx = dataArea.getCenterX();
1076: double labely = state.getCursor()
1077: + insets.getTop() + labelBounds.getHeight() / 2.0;
1078: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1079: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1080: TextAnchor.CENTER);
1081: state.cursorDown(insets.getTop() + labelBounds.getHeight()
1082: + insets.getBottom());
1083:
1084: }
1085: else if (edge == RectangleEdge.LEFT) {
1086:
1087: AffineTransform t = AffineTransform.getRotateInstance(
1088: getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(),
1089: labelBounds.getCenterY());
1090: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1091: labelBounds = rotatedLabelBounds.getBounds2D();
1092: double labelx = state.getCursor()
1093: - insets.getRight() - labelBounds.getWidth() / 2.0;
1094: double labely = dataArea.getCenterY();
1095: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1096: (float) labely, TextAnchor.CENTER,
1097: getLabelAngle() - Math.PI / 2.0, TextAnchor.CENTER);
1098: state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
1099: + insets.getRight());
1100: }
1101: else if (edge == RectangleEdge.RIGHT) {
1102:
1103: AffineTransform t = AffineTransform.getRotateInstance(
1104: getLabelAngle() + Math.PI / 2.0,
1105: labelBounds.getCenterX(), labelBounds.getCenterY());
1106: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1107: labelBounds = rotatedLabelBounds.getBounds2D();
1108: double labelx = state.getCursor()
1109: + insets.getLeft() + labelBounds.getWidth() / 2.0;
1110: double labely = dataArea.getY() + dataArea.getHeight() / 2.0;
1111: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1112: (float) labely, TextAnchor.CENTER,
1113: getLabelAngle() + Math.PI / 2.0, TextAnchor.CENTER);
1114: state.cursorRight(insets.getLeft() + labelBounds.getWidth()
1115: + insets.getRight());
1116:
1117: }
1118:
1119: return state;
1120:
1121: }
1122:
1123:
1131: protected void drawAxisLine(Graphics2D g2, double cursor,
1132: Rectangle2D dataArea, RectangleEdge edge) {
1133:
1134: Line2D axisLine = null;
1135: if (edge == RectangleEdge.TOP) {
1136: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1137: dataArea.getMaxX(), cursor);
1138: }
1139: else if (edge == RectangleEdge.BOTTOM) {
1140: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1141: dataArea.getMaxX(), cursor);
1142: }
1143: else if (edge == RectangleEdge.LEFT) {
1144: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1145: dataArea.getMaxY());
1146: }
1147: else if (edge == RectangleEdge.RIGHT) {
1148: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1149: dataArea.getMaxY());
1150: }
1151: g2.setPaint(this.axisLinePaint);
1152: g2.setStroke(this.axisLineStroke);
1153: g2.draw(axisLine);
1154:
1155: }
1156:
1157:
1165: public Object clone() throws CloneNotSupportedException {
1166: Axis clone = (Axis) super.clone();
1167:
1168: clone.plot = null;
1169: clone.listenerList = new EventListenerList();
1170: return clone;
1171: }
1172:
1173:
1180: public boolean equals(Object obj) {
1181: if (obj == this) {
1182: return true;
1183: }
1184: if (!(obj instanceof Axis)) {
1185: return false;
1186: }
1187: Axis that = (Axis) obj;
1188: if (this.visible != that.visible) {
1189: return false;
1190: }
1191: if (!ObjectUtilities.equal(this.label, that.label)) {
1192: return false;
1193: }
1194: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
1195: return false;
1196: }
1197: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
1198: return false;
1199: }
1200: if (!ObjectUtilities.equal(this.labelInsets, that.labelInsets)) {
1201: return false;
1202: }
1203: if (this.labelAngle != that.labelAngle) {
1204: return false;
1205: }
1206: if (this.axisLineVisible != that.axisLineVisible) {
1207: return false;
1208: }
1209: if (!ObjectUtilities.equal(this.axisLineStroke, that.axisLineStroke)) {
1210: return false;
1211: }
1212: if (!PaintUtilities.equal(this.axisLinePaint, that.axisLinePaint)) {
1213: return false;
1214: }
1215: if (this.tickLabelsVisible != that.tickLabelsVisible) {
1216: return false;
1217: }
1218: if (!ObjectUtilities.equal(this.tickLabelFont, that.tickLabelFont)) {
1219: return false;
1220: }
1221: if (!PaintUtilities.equal(this.tickLabelPaint, that.tickLabelPaint)) {
1222: return false;
1223: }
1224: if (!ObjectUtilities.equal(
1225: this.tickLabelInsets, that.tickLabelInsets
1226: )) {
1227: return false;
1228: }
1229: if (this.tickMarksVisible != that.tickMarksVisible) {
1230: return false;
1231: }
1232: if (this.tickMarkInsideLength != that.tickMarkInsideLength) {
1233: return false;
1234: }
1235: if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) {
1236: return false;
1237: }
1238: if (!PaintUtilities.equal(this.tickMarkPaint, that.tickMarkPaint)) {
1239: return false;
1240: }
1241: if (!ObjectUtilities.equal(this.tickMarkStroke, that.tickMarkStroke)) {
1242: return false;
1243: }
1244: if (this.fixedDimension != that.fixedDimension) {
1245: return false;
1246: }
1247: return true;
1248: }
1249:
1250:
1257: private void writeObject(ObjectOutputStream stream) throws IOException {
1258: stream.defaultWriteObject();
1259: SerialUtilities.writePaint(this.labelPaint, stream);
1260: SerialUtilities.writePaint(this.tickLabelPaint, stream);
1261: SerialUtilities.writeStroke(this.axisLineStroke, stream);
1262: SerialUtilities.writePaint(this.axisLinePaint, stream);
1263: SerialUtilities.writeStroke(this.tickMarkStroke, stream);
1264: SerialUtilities.writePaint(this.tickMarkPaint, stream);
1265: }
1266:
1267:
1275: private void readObject(ObjectInputStream stream)
1276: throws IOException, ClassNotFoundException {
1277: stream.defaultReadObject();
1278: this.labelPaint = SerialUtilities.readPaint(stream);
1279: this.tickLabelPaint = SerialUtilities.readPaint(stream);
1280: this.axisLineStroke = SerialUtilities.readStroke(stream);
1281: this.axisLinePaint = SerialUtilities.readPaint(stream);
1282: this.tickMarkStroke = SerialUtilities.readStroke(stream);
1283: this.tickMarkPaint = SerialUtilities.readPaint(stream);
1284: this.listenerList = new EventListenerList();
1285: }
1286:
1287: }