1:
82:
83: package ;
84:
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: import ;
103:
104: import ;
105:
106: import ;
107: import ;
108: import ;
109: import ;
110: import ;
111: import ;
112: import ;
113: import ;
114: import ;
115: import ;
116: import ;
117:
118:
123: public abstract class Axis implements Cloneable, Serializable {
124:
125:
126: private static final long serialVersionUID = 7719289504573298271L;
127:
128:
129: public static final boolean DEFAULT_AXIS_VISIBLE = true;
130:
131:
132: public static final Font DEFAULT_AXIS_LABEL_FONT
133: = new Font("SansSerif", Font.PLAIN, 12);
134:
135:
136: public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.black;
137:
138:
139: public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS
140: = new RectangleInsets(3.0, 3.0, 3.0, 3.0);
141:
142:
143: public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.gray;
144:
145:
146: public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(1.0f);
147:
148:
149: public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true;
150:
151:
152: public static final Font DEFAULT_TICK_LABEL_FONT
153: = new Font("SansSerif", Font.PLAIN, 10);
154:
155:
156: public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.black;
157:
158:
159: public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS
160: = new RectangleInsets(2.0, 4.0, 2.0, 4.0);
161:
162:
163: public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true;
164:
165:
166: public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(1);
167:
168:
169: public static final Paint DEFAULT_TICK_MARK_PAINT = Color.gray;
170:
171:
172: public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f;
173:
174:
175: public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f;
176:
177:
178: private boolean visible;
179:
180:
181: private String label;
182:
183:
184: private Font labelFont;
185:
186:
187: private transient Paint labelPaint;
188:
189:
190: private RectangleInsets labelInsets;
191:
192:
193: private double labelAngle;
194:
195:
196: private boolean axisLineVisible;
197:
198:
199: private transient Stroke axisLineStroke;
200:
201:
202: private transient Paint axisLinePaint;
203:
204:
208: private boolean tickLabelsVisible;
209:
210:
211: private Font tickLabelFont;
212:
213:
214: private transient Paint tickLabelPaint;
215:
216:
217: private RectangleInsets tickLabelInsets;
218:
219:
223: private boolean tickMarksVisible;
224:
225:
226: private float tickMarkInsideLength;
227:
228:
229: private float tickMarkOutsideLength;
230:
231:
232: private transient Stroke tickMarkStroke;
233:
234:
235: private transient Paint tickMarkPaint;
236:
237:
238: private double fixedDimension;
239:
240:
244: private transient Plot plot;
245:
246:
247: private transient EventListenerList listenerList;
248:
249:
254: protected Axis(String label) {
255:
256: this.label = label;
257: this.visible = DEFAULT_AXIS_VISIBLE;
258: this.labelFont = DEFAULT_AXIS_LABEL_FONT;
259: this.labelPaint = DEFAULT_AXIS_LABEL_PAINT;
260: this.labelInsets = DEFAULT_AXIS_LABEL_INSETS;
261: this.labelAngle = 0.0;
262:
263: this.axisLineVisible = true;
264: this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT;
265: this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE;
266:
267: this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE;
268: this.tickLabelFont = DEFAULT_TICK_LABEL_FONT;
269: this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT;
270: this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS;
271:
272: this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE;
273: this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE;
274: this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT;
275: this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;
276: this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;
277:
278: this.plot = null;
279:
280: this.listenerList = new EventListenerList();
281:
282: }
283:
284:
292: public boolean isVisible() {
293: return this.visible;
294: }
295:
296:
304: public void setVisible(boolean flag) {
305: if (flag != this.visible) {
306: this.visible = flag;
307: notifyListeners(new AxisChangeEvent(this));
308: }
309: }
310:
311:
320: public String getLabel() {
321: return this.label;
322: }
323:
324:
334: public void setLabel(String label) {
335:
336: String existing = this.label;
337: if (existing != null) {
338: if (!existing.equals(label)) {
339: this.label = label;
340: notifyListeners(new AxisChangeEvent(this));
341: }
342: }
343: else {
344: if (label != null) {
345: this.label = label;
346: notifyListeners(new AxisChangeEvent(this));
347: }
348: }
349:
350: }
351:
352:
359: public Font getLabelFont() {
360: return this.labelFont;
361: }
362:
363:
371: public void setLabelFont(Font font) {
372: if (font == null) {
373: throw new IllegalArgumentException("Null 'font' argument.");
374: }
375: if (!this.labelFont.equals(font)) {
376: this.labelFont = font;
377: notifyListeners(new AxisChangeEvent(this));
378: }
379: }
380:
381:
388: public Paint getLabelPaint() {
389: return this.labelPaint;
390: }
391:
392:
400: public void setLabelPaint(Paint paint) {
401: if (paint == null) {
402: throw new IllegalArgumentException("Null 'paint' argument.");
403: }
404: this.labelPaint = paint;
405: notifyListeners(new AxisChangeEvent(this));
406: }
407:
408:
416: public RectangleInsets getLabelInsets() {
417: return this.labelInsets;
418: }
419:
420:
428: public void setLabelInsets(RectangleInsets insets) {
429: if (insets == null) {
430: throw new IllegalArgumentException("Null 'insets' argument.");
431: }
432: if (!insets.equals(this.labelInsets)) {
433: this.labelInsets = insets;
434: notifyListeners(new AxisChangeEvent(this));
435: }
436: }
437:
438:
445: public double getLabelAngle() {
446: return this.labelAngle;
447: }
448:
449:
457: public void setLabelAngle(double angle) {
458: this.labelAngle = angle;
459: notifyListeners(new AxisChangeEvent(this));
460: }
461:
462:
471: public boolean isAxisLineVisible() {
472: return this.axisLineVisible;
473: }
474:
475:
485: public void setAxisLineVisible(boolean visible) {
486: this.axisLineVisible = visible;
487: notifyListeners(new AxisChangeEvent(this));
488: }
489:
490:
497: public Paint getAxisLinePaint() {
498: return this.axisLinePaint;
499: }
500:
501:
509: public void setAxisLinePaint(Paint paint) {
510: if (paint == null) {
511: throw new IllegalArgumentException("Null 'paint' argument.");
512: }
513: this.axisLinePaint = paint;
514: notifyListeners(new AxisChangeEvent(this));
515: }
516:
517:
524: public Stroke getAxisLineStroke() {
525: return this.axisLineStroke;
526: }
527:
528:
536: public void setAxisLineStroke(Stroke stroke) {
537: if (stroke == null) {
538: throw new IllegalArgumentException("Null 'stroke' argument.");
539: }
540: this.axisLineStroke = stroke;
541: notifyListeners(new AxisChangeEvent(this));
542: }
543:
544:
553: public boolean isTickLabelsVisible() {
554: return this.tickLabelsVisible;
555: }
556:
557:
568: public void setTickLabelsVisible(boolean flag) {
569:
570: if (flag != this.tickLabelsVisible) {
571: this.tickLabelsVisible = flag;
572: notifyListeners(new AxisChangeEvent(this));
573: }
574:
575: }
576:
577:
584: public Font getTickLabelFont() {
585: return this.tickLabelFont;
586: }
587:
588:
596: public void setTickLabelFont(Font font) {
597:
598: if (font == null) {
599: throw new IllegalArgumentException("Null 'font' argument.");
600: }
601:
602: if (!this.tickLabelFont.equals(font)) {
603: this.tickLabelFont = font;
604: notifyListeners(new AxisChangeEvent(this));
605: }
606:
607: }
608:
609:
616: public Paint getTickLabelPaint() {
617: return this.tickLabelPaint;
618: }
619:
620:
628: public void setTickLabelPaint(Paint paint) {
629: if (paint == null) {
630: throw new IllegalArgumentException("Null 'paint' argument.");
631: }
632: this.tickLabelPaint = paint;
633: notifyListeners(new AxisChangeEvent(this));
634: }
635:
636:
643: public RectangleInsets getTickLabelInsets() {
644: return this.tickLabelInsets;
645: }
646:
647:
655: public void setTickLabelInsets(RectangleInsets insets) {
656: if (insets == null) {
657: throw new IllegalArgumentException("Null 'insets' argument.");
658: }
659: if (!this.tickLabelInsets.equals(insets)) {
660: this.tickLabelInsets = insets;
661: notifyListeners(new AxisChangeEvent(this));
662: }
663: }
664:
665:
674: public boolean isTickMarksVisible() {
675: return this.tickMarksVisible;
676: }
677:
678:
686: public void setTickMarksVisible(boolean flag) {
687: if (flag != this.tickMarksVisible) {
688: this.tickMarksVisible = flag;
689: notifyListeners(new AxisChangeEvent(this));
690: }
691: }
692:
693:
701: public float getTickMarkInsideLength() {
702: return this.tickMarkInsideLength;
703: }
704:
705:
713: public void setTickMarkInsideLength(float length) {
714: this.tickMarkInsideLength = length;
715: notifyListeners(new AxisChangeEvent(this));
716: }
717:
718:
726: public float getTickMarkOutsideLength() {
727: return this.tickMarkOutsideLength;
728: }
729:
730:
738: public void setTickMarkOutsideLength(float length) {
739: this.tickMarkOutsideLength = length;
740: notifyListeners(new AxisChangeEvent(this));
741: }
742:
743:
750: public Stroke getTickMarkStroke() {
751: return this.tickMarkStroke;
752: }
753:
754:
762: public void setTickMarkStroke(Stroke stroke) {
763: if (stroke == null) {
764: throw new IllegalArgumentException("Null 'stroke' argument.");
765: }
766: if (!this.tickMarkStroke.equals(stroke)) {
767: this.tickMarkStroke = stroke;
768: notifyListeners(new AxisChangeEvent(this));
769: }
770: }
771:
772:
779: public Paint getTickMarkPaint() {
780: return this.tickMarkPaint;
781: }
782:
783:
791: public void setTickMarkPaint(Paint paint) {
792: if (paint == null) {
793: throw new IllegalArgumentException("Null 'paint' argument.");
794: }
795: this.tickMarkPaint = paint;
796: notifyListeners(new AxisChangeEvent(this));
797: }
798:
799:
808: public Plot getPlot() {
809: return this.plot;
810: }
811:
812:
821: public void setPlot(Plot plot) {
822: this.plot = plot;
823: configure();
824: }
825:
826:
833: public double getFixedDimension() {
834: return this.fixedDimension;
835: }
836:
837:
850: public void setFixedDimension(double dimension) {
851: this.fixedDimension = dimension;
852: }
853:
854:
858: public abstract void configure();
859:
860:
873: public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot,
874: Rectangle2D plotArea,
875: RectangleEdge edge,
876: AxisSpace space);
877:
878:
892: public abstract AxisState draw(Graphics2D g2,
893: double cursor,
894: Rectangle2D plotArea,
895: Rectangle2D dataArea,
896: RectangleEdge edge,
897: PlotRenderingInfo plotState);
898:
899:
910: public abstract List refreshTicks(Graphics2D g2,
911: AxisState state,
912: Rectangle2D dataArea,
913: RectangleEdge edge);
914:
915:
922: public void addChangeListener(AxisChangeListener listener) {
923: this.listenerList.add(AxisChangeListener.class, listener);
924: }
925:
926:
933: public void removeChangeListener(AxisChangeListener listener) {
934: this.listenerList.remove(AxisChangeListener.class, listener);
935: }
936:
937:
946: public boolean hasListener(EventListener listener) {
947: List list = Arrays.asList(this.listenerList.getListenerList());
948: return list.contains(listener);
949: }
950:
951:
957: protected void notifyListeners(AxisChangeEvent event) {
958:
959: Object[] listeners = this.listenerList.getListenerList();
960: for (int i = listeners.length - 2; i >= 0; i -= 2) {
961: if (listeners[i] == AxisChangeListener.class) {
962: ((AxisChangeListener) listeners[i + 1]).axisChanged(event);
963: }
964: }
965:
966: }
967:
968:
977: protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
978:
979: Rectangle2D result = new Rectangle2D.Double();
980: String axisLabel = getLabel();
981: if (axisLabel != null && !axisLabel.equals("")) {
982: FontMetrics fm = g2.getFontMetrics(getLabelFont());
983: Rectangle2D bounds = TextUtilities.getTextBounds(axisLabel, g2, fm);
984: RectangleInsets insets = getLabelInsets();
985: bounds = insets.createOutsetRectangle(bounds);
986: double angle = getLabelAngle();
987: if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) {
988: angle = angle - Math.PI / 2.0;
989: }
990: double x = bounds.getCenterX();
991: double y = bounds.getCenterY();
992: AffineTransform transformer
993: = AffineTransform.getRotateInstance(angle, x, y);
994: Shape labelBounds = transformer.createTransformedShape(bounds);
995: result = labelBounds.getBounds2D();
996: }
997:
998: return result;
999:
1000: }
1001:
1002:
1014: protected AxisState drawLabel(String label,
1015: Graphics2D g2,
1016: Rectangle2D plotArea,
1017: Rectangle2D dataArea,
1018: RectangleEdge edge,
1019: AxisState state) {
1020:
1021:
1022: if (state == null) {
1023: throw new IllegalArgumentException("Null 'state' argument.");
1024: }
1025:
1026: if ((label == null) || (label.equals(""))) {
1027: return state;
1028: }
1029:
1030: Font font = getLabelFont();
1031: RectangleInsets insets = getLabelInsets();
1032: g2.setFont(font);
1033: g2.setPaint(getLabelPaint());
1034: FontMetrics fm = g2.getFontMetrics();
1035: Rectangle2D labelBounds = TextUtilities.getTextBounds(label, g2, fm);
1036:
1037: if (edge == RectangleEdge.TOP) {
1038:
1039: AffineTransform t = AffineTransform.getRotateInstance(
1040: getLabelAngle(), labelBounds.getCenterX(),
1041: labelBounds.getCenterY());
1042: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1043: labelBounds = rotatedLabelBounds.getBounds2D();
1044: double labelx = dataArea.getCenterX();
1045: double labely = state.getCursor() - insets.getBottom()
1046: - labelBounds.getHeight() / 2.0;
1047: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1048: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1049: TextAnchor.CENTER);
1050: state.cursorUp(insets.getTop() + labelBounds.getHeight()
1051: + insets.getBottom());
1052:
1053: }
1054: else if (edge == RectangleEdge.BOTTOM) {
1055:
1056: AffineTransform t = AffineTransform.getRotateInstance(
1057: getLabelAngle(), labelBounds.getCenterX(),
1058: labelBounds.getCenterY());
1059: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1060: labelBounds = rotatedLabelBounds.getBounds2D();
1061: double labelx = dataArea.getCenterX();
1062: double labely = state.getCursor()
1063: + insets.getTop() + labelBounds.getHeight() / 2.0;
1064: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1065: (float) labely, TextAnchor.CENTER, getLabelAngle(),
1066: TextAnchor.CENTER);
1067: state.cursorDown(insets.getTop() + labelBounds.getHeight()
1068: + insets.getBottom());
1069:
1070: }
1071: else if (edge == RectangleEdge.LEFT) {
1072:
1073: AffineTransform t = AffineTransform.getRotateInstance(
1074: getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(),
1075: labelBounds.getCenterY());
1076: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1077: labelBounds = rotatedLabelBounds.getBounds2D();
1078: double labelx = state.getCursor()
1079: - insets.getRight() - labelBounds.getWidth() / 2.0;
1080: double labely = dataArea.getCenterY();
1081: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1082: (float) labely, TextAnchor.CENTER,
1083: getLabelAngle() - Math.PI / 2.0, TextAnchor.CENTER);
1084: state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
1085: + insets.getRight());
1086: }
1087: else if (edge == RectangleEdge.RIGHT) {
1088:
1089: AffineTransform t = AffineTransform.getRotateInstance(
1090: getLabelAngle() + Math.PI / 2.0,
1091: labelBounds.getCenterX(), labelBounds.getCenterY());
1092: Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
1093: labelBounds = rotatedLabelBounds.getBounds2D();
1094: double labelx = state.getCursor()
1095: + insets.getLeft() + labelBounds.getWidth() / 2.0;
1096: double labely = dataArea.getY() + dataArea.getHeight() / 2.0;
1097: TextUtilities.drawRotatedString(label, g2, (float) labelx,
1098: (float) labely, TextAnchor.CENTER,
1099: getLabelAngle() + Math.PI / 2.0, TextAnchor.CENTER);
1100: state.cursorRight(insets.getLeft() + labelBounds.getWidth()
1101: + insets.getRight());
1102:
1103: }
1104:
1105: return state;
1106:
1107: }
1108:
1109:
1117: protected void drawAxisLine(Graphics2D g2, double cursor,
1118: Rectangle2D dataArea, RectangleEdge edge) {
1119:
1120: Line2D axisLine = null;
1121: if (edge == RectangleEdge.TOP) {
1122: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1123: dataArea.getMaxX(), cursor);
1124: }
1125: else if (edge == RectangleEdge.BOTTOM) {
1126: axisLine = new Line2D.Double(dataArea.getX(), cursor,
1127: dataArea.getMaxX(), cursor);
1128: }
1129: else if (edge == RectangleEdge.LEFT) {
1130: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1131: dataArea.getMaxY());
1132: }
1133: else if (edge == RectangleEdge.RIGHT) {
1134: axisLine = new Line2D.Double(cursor, dataArea.getY(), cursor,
1135: dataArea.getMaxY());
1136: }
1137: g2.setPaint(this.axisLinePaint);
1138: g2.setStroke(this.axisLineStroke);
1139: g2.draw(axisLine);
1140:
1141: }
1142:
1143:
1151: public Object clone() throws CloneNotSupportedException {
1152: Axis clone = (Axis) super.clone();
1153:
1154: clone.plot = null;
1155: clone.listenerList = new EventListenerList();
1156: return clone;
1157: }
1158:
1159:
1166: public boolean equals(Object obj) {
1167: if (obj == this) {
1168: return true;
1169: }
1170: if (!(obj instanceof Axis)) {
1171: return false;
1172: }
1173: Axis that = (Axis) obj;
1174: if (this.visible != that.visible) {
1175: return false;
1176: }
1177: if (!ObjectUtilities.equal(this.label, that.label)) {
1178: return false;
1179: }
1180: if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
1181: return false;
1182: }
1183: if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
1184: return false;
1185: }
1186: if (!ObjectUtilities.equal(this.labelInsets, that.labelInsets)) {
1187: return false;
1188: }
1189: if (this.labelAngle != that.labelAngle) {
1190: return false;
1191: }
1192: if (this.axisLineVisible != that.axisLineVisible) {
1193: return false;
1194: }
1195: if (!ObjectUtilities.equal(this.axisLineStroke, that.axisLineStroke)) {
1196: return false;
1197: }
1198: if (!PaintUtilities.equal(this.axisLinePaint, that.axisLinePaint)) {
1199: return false;
1200: }
1201: if (this.tickLabelsVisible != that.tickLabelsVisible) {
1202: return false;
1203: }
1204: if (!ObjectUtilities.equal(this.tickLabelFont, that.tickLabelFont)) {
1205: return false;
1206: }
1207: if (!PaintUtilities.equal(this.tickLabelPaint, that.tickLabelPaint)) {
1208: return false;
1209: }
1210: if (!ObjectUtilities.equal(
1211: this.tickLabelInsets, that.tickLabelInsets
1212: )) {
1213: return false;
1214: }
1215: if (this.tickMarksVisible != that.tickMarksVisible) {
1216: return false;
1217: }
1218: if (this.tickMarkInsideLength != that.tickMarkInsideLength) {
1219: return false;
1220: }
1221: if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) {
1222: return false;
1223: }
1224: if (!PaintUtilities.equal(this.tickMarkPaint, that.tickMarkPaint)) {
1225: return false;
1226: }
1227: if (!ObjectUtilities.equal(this.tickMarkStroke, that.tickMarkStroke)) {
1228: return false;
1229: }
1230: if (this.fixedDimension != that.fixedDimension) {
1231: return false;
1232: }
1233: return true;
1234: }
1235:
1236:
1243: private void writeObject(ObjectOutputStream stream) throws IOException {
1244: stream.defaultWriteObject();
1245: SerialUtilities.writePaint(this.labelPaint, stream);
1246: SerialUtilities.writePaint(this.tickLabelPaint, stream);
1247: SerialUtilities.writeStroke(this.axisLineStroke, stream);
1248: SerialUtilities.writePaint(this.axisLinePaint, stream);
1249: SerialUtilities.writeStroke(this.tickMarkStroke, stream);
1250: SerialUtilities.writePaint(this.tickMarkPaint, stream);
1251: }
1252:
1253:
1261: private void readObject(ObjectInputStream stream)
1262: throws IOException, ClassNotFoundException {
1263: stream.defaultReadObject();
1264: this.labelPaint = SerialUtilities.readPaint(stream);
1265: this.tickLabelPaint = SerialUtilities.readPaint(stream);
1266: this.axisLineStroke = SerialUtilities.readStroke(stream);
1267: this.axisLinePaint = SerialUtilities.readPaint(stream);
1268: this.tickMarkStroke = SerialUtilities.readStroke(stream);
1269: this.tickMarkPaint = SerialUtilities.readPaint(stream);
1270: this.listenerList = new EventListenerList();
1271: }
1272:
1273: }