001: /*
002: JOpenChart Java Charting Library and Toolkit
003: Copyright (C) 2001 Sebastian Müller
004: http://jopenchart.sourceforge.net
005:
006: This library is free software; you can redistribute it and/or
007: modify it under the terms of the GNU Lesser General Public
008: License as published by the Free Software Foundation; either
009: version 2.1 of the License, or (at your option) any later version.
010:
011: This library is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public
017: License along with this library; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019:
020: CoordSystemUtilities.java
021: Created on 4. April 2002, 22:28
022: */
023:
024: package de.progra.charting;
025:
026: import java.awt.font.FontRenderContext;
027: import java.text.DecimalFormat;
028: import java.awt.geom.AffineTransform;
029: import java.awt.geom.Line2D;
030: import java.awt.geom.Point2D;
031: import java.awt.geom.Rectangle2D;
032: import java.awt.Color;
033: import java.awt.Font;
034: import java.awt.Graphics2D;
035: import java.awt.font.TextLayout;
036: import de.progra.charting.model.ChartDataModelConstraints;
037: import de.progra.charting.model.ChartDataModel;
038:
039: /**
040: * This class provides some utility functions for a CoordSystem. They were
041: * externalized to make the CoordSystem class clearer.
042: * @author mueller
043: * @version 1.0
044: */
045: public class CoordSystemUtilities {
046:
047: /** used for the offset on the y axis for the size of a "tick"*/
048: protected final int marginOffset = 6;
049:
050: protected CoordSystem c;
051: protected ChartDataModelConstraints constraints;
052: protected ChartDataModelConstraints constraints2;
053: protected ChartDataModel model;
054:
055: /** Creates a new instance of CoordSystemUtilities */
056: public CoordSystemUtilities(CoordSystem coord,
057: ChartDataModelConstraints constraints,
058: ChartDataModelConstraints constraints2, ChartDataModel model) {
059: c = coord;
060: this .constraints = constraints;
061: this .constraints2 = constraints2;
062: this .model = model;
063: }
064:
065: /** Computes the left margin. */
066: public int computeLeftMargin() {
067: double xmin = constraints.getMinimumColumnValue();
068: double xmax = constraints.getMaximumColumnValue();
069:
070: if (xmin <= 0 && xmax > 0) {
071: xmin = Math.abs(xmin);
072: xmax = Math.abs(xmax);
073:
074: TextLayout layout = new TextLayout(c.getYAxisUnit(), c
075: .getFont(), c.getFontRenderContext());
076:
077: // yaxis label width
078: int maxlmargin = computeYAxisLabelWidth() + marginOffset; // + yaxis title width
079:
080: // unit width
081: maxlmargin = Math.max(maxlmargin, (int) layout.getBounds()
082: .getWidth()
083: + marginOffset);
084:
085: int margin = (int) (maxlmargin - (xmin / (xmin + xmax))
086: * (c.getBounds().getWidth() - c.getRightMargin()));
087:
088: margin += 5; // just for good looking
089:
090: if (margin < c.MINIMALMARGIN)
091: margin = c.MINIMALMARGIN;
092:
093: return margin;
094: } else {
095: return c.MINIMALMARGIN;
096: }
097: }
098:
099: /** Computes the right margin. */
100: public int computeRightMargin() {
101: TextLayout layout = new TextLayout(c.getXAxisUnit(), c
102: .getFont(), c.getFontRenderContext());
103: return Math
104: .max(
105: (int) (layout.getBounds().getWidth() + (double) c.ARROWLENGTH / 3),
106: c.ARROWLENGTH);
107: }
108:
109: /** Computes the top margin. */
110: public int computeTopMargin() {
111: TextLayout layout = new TextLayout(c.getYAxisUnit(), c
112: .getFont(), c.getFontRenderContext());
113: return Math.max((int) (layout.getBounds().getHeight()
114: + (double) c.ARROWLENGTH / 3 + layout.getDescent()),
115: c.ARROWLENGTH);
116: }
117:
118: /** Computes the bottom margin. */
119: public int computeBottomMargin() {
120: double ymin = constraints.getMinimumValue().doubleValue();
121: double ymax = constraints.getMaximumValue().doubleValue();
122:
123: if (ymin <= 0 && ymax > 0) {
124: ymin = Math.abs(ymin);
125: ymax = Math.abs(ymax);
126:
127: TextLayout layout = new TextLayout(c.getXAxisUnit(), c
128: .getFont(), c.getFontRenderContext());
129:
130: // xaxis label height
131: int maxbmargin = computeXAxisLabelHeight() + marginOffset; // + xaxis title height
132:
133: // unit height
134: maxbmargin = Math.max(maxbmargin, (int) layout.getBounds()
135: .getHeight()
136: + marginOffset);
137:
138: int margin = (int) (maxbmargin - (ymin / (ymin + ymax))
139: * (c.getBounds().getHeight() - c.getTopMargin()));
140:
141: margin += 10; // just for good looking
142:
143: if (margin < c.MINIMALMARGIN)
144: margin = c.MINIMALMARGIN;
145:
146: return margin;
147: } else {
148: return c.MINIMALMARGIN;
149: }
150: }
151:
152: /** Computes the maximum height of all x-axis labels. */
153: public int computeXAxisLabelHeight() {
154: double min = constraints.getMinimumColumnValue();
155: double max = constraints.getMaximumColumnValue();
156: double tick = ChartUtilities.calculateTickSpacing(min, max);
157: double ypt = 0;
158:
159: int height = 0;
160:
161: if (constraints.getMinimumValue().doubleValue() > 0)
162: ypt = constraints.getMinimumValue().doubleValue();
163: else if (constraints.getMaximumValue().doubleValue() < 0)
164: ypt = constraints.getMaximumValue().doubleValue();
165: boolean paint = false;
166:
167: DecimalFormat df = c.getXDecimalFormat();
168: FontRenderContext frc = c.getFontRenderContext();
169: Font f = c.getFont();
170:
171: for (double d = min; d <= max; d += tick) {
172: if (paint) {
173: String sb = df.format(d);
174: Rectangle2D r = f.getStringBounds(sb, frc);
175:
176: height = Math.max(height, (int) r.getHeight());
177: }
178: paint = !paint;
179: }
180:
181: return height;
182: }
183:
184: /** Computes the maximum width of all y-axis labels. */
185: public int computeYAxisLabelWidth() {
186: double min = constraints.getMinimumValue().doubleValue();
187: double max = constraints.getMaximumValue().doubleValue();
188: double tick = ChartUtilities.calculateTickSpacing(min, max);
189: double xpt = 0;
190:
191: int width = 0;
192:
193: // shift the y-axis according to the max and min x-values
194: if (constraints.getMinimumColumnValue() > 0)
195: xpt = constraints.getMinimumColumnValue();
196: else if (constraints.getMaximumColumnValue() < 0
197: && c.getSecondYAxis() != null)
198: xpt = constraints.getMaximumColumnValue();
199:
200: boolean paint = false;
201:
202: DecimalFormat df = c.getYDecimalFormat();
203: FontRenderContext frc = c.getFontRenderContext();
204: Font f = c.getFont();
205:
206: for (double d = min; d <= max; d += tick) {
207: if (paint) {
208: String sb = df.format(d);
209: Rectangle2D r = f.getStringBounds(sb, frc);
210:
211: width = Math.max((int) r.getWidth(), width);
212: }
213: paint = !paint;
214: }
215:
216: return width;
217: }
218:
219: /** This method is called by paintDefault to paint the ticks on the
220: * x-axis for numerical x-axis values.
221: * @param g the Graphics2D context to paint in
222: */
223: public void drawNumericalXAxisTicks(Graphics2D g) {
224: AffineTransform at = c.getTransform(CoordSystem.FIRST_YAXIS);
225:
226: double min = constraints.getMinimumColumnValue();
227: double max = constraints.getMaximumColumnValue();
228: double tick = ChartUtilities.calculateTickSpacing(min, max);
229: double ypt = 0;
230:
231: if (constraints.getMinimumValue().doubleValue() > 0)
232: ypt = constraints.getMinimumValue().doubleValue();
233: else if (constraints.getMaximumValue().doubleValue() < 0)
234: ypt = constraints.getMaximumValue().doubleValue();
235:
236: Point2D p = new Point2D.Double(0.0, 0.0);
237: Point2D v;
238: Line2D ticks = new Line2D.Double(0.0, 0.0, 0.0, 0.0);
239:
240: DecimalFormat df = c.getXDecimalFormat();
241: FontRenderContext frc = c.getFontRenderContext();
242: Font f = c.getFont();
243:
244: boolean paint = false;
245:
246: g.setFont(f);
247: boolean paintLabels = c.isPaintLabels();
248:
249: for (double d = min; d <= max; d += tick) {
250: p.setLocation(d, ypt);
251: v = at.transform(p, null);
252:
253: ticks.setLine(v.getX(), v.getY() - marginOffset / 2, v
254: .getX(), v.getY() + marginOffset / 2);
255: g.draw(ticks);
256: if (paint && paintLabels) {
257: String sb = df.format(d);
258: Rectangle2D r = f.getStringBounds(sb, frc);
259:
260: g
261: .drawString(sb, (float) (v.getX() - r
262: .getWidth() / 2), (float) (v.getY()
263: + r.getHeight() + marginOffset));
264: }
265: paint = !paint;
266: }
267: }
268:
269: /** This method is called by paintDefault to paint the ticks on the
270: * x-axis for non-numerical x-axis values..
271: * @param g the Graphics2D context to paint in
272: */
273: public void drawXAxisTicks(Graphics2D g) {
274: AffineTransform at = c.getTransform(CoordSystem.FIRST_YAXIS);
275:
276: int min = (int) constraints.getMinimumColumnValue();
277: int max = (int) constraints.getMaximumColumnValue();
278: double tick = 1.0;
279: double ypt = 0;
280:
281: if (constraints.getMinimumValue().doubleValue() > 0)
282: ypt = constraints.getMinimumValue().doubleValue();
283: else if (constraints.getMaximumValue().doubleValue() < 0)
284: ypt = constraints.getMaximumValue().doubleValue();
285:
286: Point2D p = new Point2D.Double(0.0, 0.0);
287: Point2D v = null;
288: Point2D oldv = null;
289:
290: Line2D ticks = new Line2D.Double(0.0, 0.0, 0.0, 0.0);
291:
292: DecimalFormat df = c.getXDecimalFormat();
293: FontRenderContext frc = c.getFontRenderContext();
294: Font f = c.getFont();
295:
296: boolean paint = false;
297: boolean paintLabels = c.isPaintLabels();
298: g.setFont(f);
299:
300: for (int i = min - 1; i < max; i++) {
301: p.setLocation(i + 1, ypt);
302:
303: v = at.transform(p, null);
304:
305: ticks.setLine(v.getX(), v.getY() - marginOffset / 2, v
306: .getX(), v.getY() + marginOffset / 2);
307:
308: if (i + 1 < max)
309: g.draw(ticks);
310:
311: // Draw Strings between ticks
312: if (oldv != null && paintLabels) {
313: String sb = (String) model.getColumnValueAt(i);
314: Rectangle2D r = f.getStringBounds(sb, frc);
315:
316: g.drawString(sb,
317: (float) (oldv.getX() + (v.getX() - oldv.getX())
318: / 2 - r.getWidth() / 2), (float) (v
319: .getY()
320: + r.getHeight() + marginOffset));
321: }
322:
323: oldv = v;
324: }
325: }
326:
327: /** This method is called by paintDefault to paint the ticks on the
328: * y-axis.
329: * @param g the Graphics2D context in which to draw
330: */
331: public void drawYAxisTicks(Graphics2D g) {
332: AffineTransform at = c.getTransform(CoordSystem.FIRST_YAXIS);
333:
334: double min = constraints.getMinimumValue().doubleValue();
335: double max = constraints.getMaximumValue().doubleValue();
336: double tick = ChartUtilities.calculateTickSpacing(min, max);
337: double xpt = 0;
338:
339: // shift the y-axis according to the max and min x-values
340: if (constraints.getMinimumColumnValue() > 0)
341: xpt = constraints.getMinimumColumnValue();
342: else if (constraints.getMaximumColumnValue() < 0
343: && c.getSecondYAxis() != null)
344: xpt = constraints.getMaximumColumnValue();
345:
346: Point2D p = new Point2D.Double(0.0, 0.0);
347: Point2D v;
348: Line2D ticks = new Line2D.Double(0.0, 0.0, 0.0, 0.0);
349: boolean paint = false;
350:
351: DecimalFormat df = c.getYDecimalFormat();
352: FontRenderContext frc = c.getFontRenderContext();
353: Font f = c.getFont();
354:
355: Color backupColor = g.getColor();
356: g.setFont(f);
357: boolean paintLabels = c.isPaintLabels();
358:
359: for (double d = min; d <= max; d += tick) {
360: p.setLocation(xpt, d);
361: v = at.transform(p, null);
362:
363: ticks.setLine(v.getX() - marginOffset / 2, v.getY(), v
364: .getX()
365: + marginOffset / 2, v.getY());
366:
367: g.draw(ticks);
368:
369: if (d != min && !c.isPaintOnlyTick()) {
370: Line2D xax = getXAxisLine2D();
371: ticks.setLine(v.getX() + marginOffset / 2, v.getY(),
372: xax.getX2(), v.getY());
373: g.setColor(Color.lightGray);
374: g.draw(ticks);
375: g.setColor(backupColor);
376: }
377:
378: if (paintLabels && (paint || !c.isPaintAltTick())) {
379: String sb = df.format(d);
380: Rectangle2D r = f.getStringBounds(sb, frc);
381:
382: g
383: .drawString(sb, (float) (v.getX()
384: - r.getWidth() - marginOffset),
385: (float) (v.getY() + r.getHeight() / 2));
386: }
387: paint = !paint;
388: }
389: }
390:
391: /** Computes the Line2D object of the x-axis using the DataConstraints.*/
392: public Line2D getXAxisLine2D() {
393: double ypt = 0.0;
394: // shift the x-axis according to the max and min y-values
395: if (constraints.getMinimumValue().doubleValue() > 0)
396: ypt = constraints.getMinimumValue().doubleValue();
397: else if (constraints.getMaximumValue().doubleValue() < 0)
398: ypt = constraints.getMaximumValue().doubleValue();
399:
400: AffineTransform at = c.getTransform(CoordSystem.FIRST_YAXIS);
401:
402: Point2D l = at.transform(new Point2D.Double(constraints
403: .getMinimumColumnValue(), ypt), null);
404: Point2D r = at.transform(new Point2D.Double(constraints
405: .getMaximumColumnValue(), ypt), null);
406:
407: return new Line2D.Double(l, r);
408: }
409:
410: /** Computes the Line2D object of the y-axis using the DataConstraints.*/
411: public Line2D getYAxisLine2D() {
412: double xpt = 0.0;
413:
414: // shift the y-axis according to the max and min x-values
415: if (constraints.getMinimumColumnValue() > 0)
416: xpt = constraints.getMinimumColumnValue();
417: else if (constraints.getMaximumColumnValue() < 0
418: && c.getSecondYAxis() != null)
419: xpt = constraints.getMaximumColumnValue();
420:
421: AffineTransform at = c.getTransform(CoordSystem.FIRST_YAXIS);
422:
423: Point2D o = at.transform(new Point2D.Double(xpt, constraints
424: .getMaximumValue().doubleValue()), null);
425: Point2D u = at.transform(new Point2D.Double(xpt, constraints
426: .getMinimumValue().doubleValue()), null);
427: //System.out.println("** Y-Axis ("+o+", "+u+")");
428: return new Line2D.Double(o, u);
429: }
430:
431: /** Computes the Line2D object of the second y-axis using the DataConstraints.*/
432: public Line2D getSecondYAxisLine2D() {
433: double xpt = constraints2.getMaximumColumnValue();
434:
435: AffineTransform at = c.getTransform(CoordSystem.SECOND_YAXIS);
436:
437: Point2D o = at.transform(new Point2D.Double(xpt, constraints2
438: .getMaximumValue().doubleValue()), null);
439: Point2D u = at.transform(new Point2D.Double(xpt, constraints2
440: .getMinimumValue().doubleValue()), null);
441:
442: return new Line2D.Double(o, u);
443: }
444:
445: }
|