001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ---------------------------------
028: * XYSmoothLineAndShapeRenderer.java
029: * ---------------------------------
030: * (C) Copyright 2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: -;
033: * Contributor(s): -;
034: *
035: * $Id: XYSmoothLineAndShapeRenderer.java,v 1.1.2.1 2007/06/14 09:32:00 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 14-Jun-2007 : Version 1;
040: *
041: */
042:
043: package org.jfree.experimental.chart.renderer.xy;
044:
045: import java.awt.Graphics2D;
046: import java.awt.geom.Point2D;
047: import java.awt.geom.Rectangle2D;
048:
049: import org.jfree.chart.axis.ValueAxis;
050: import org.jfree.chart.entity.EntityCollection;
051: import org.jfree.chart.plot.CrosshairState;
052: import org.jfree.chart.plot.PlotOrientation;
053: import org.jfree.chart.plot.XYPlot;
054: import org.jfree.chart.renderer.xy.XYItemRendererState;
055: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
056: import org.jfree.data.xy.XYDataset;
057: import org.jfree.ui.RectangleEdge;
058:
059: /**
060: * A line and shape renderer that performs line smoothing. See
061: * http://www.jfree.org/phpBB2/viewtopic.php?t=20671
062: *
063: * WARNING: THIS CLASS IS NOT PART OF THE STANDARD JFREECHART API AND IS
064: * SUBJECT TO ALTERATION OR REMOVAL. DO NOT RELY ON THIS CLASS FOR
065: * PRODUCTION USE. Please experiment with this code and provide feedback.
066: */
067: public class XYSmoothLineAndShapeRenderer extends
068: XYLineAndShapeRenderer {
069:
070: protected void drawPrimaryLine(XYItemRendererState state,
071: Graphics2D g2, XYPlot plot, XYDataset dataset, int pass,
072: int series, int item, ValueAxis domainAxis,
073: ValueAxis rangeAxis, Rectangle2D dataArea) {
074:
075: if (item == 0) {
076: return;
077: }
078:
079: // get the data point...
080: double x1 = dataset.getXValue(series, item);
081: double y1 = dataset.getYValue(series, item);
082: if (Double.isNaN(y1) || Double.isNaN(x1)) {
083: return;
084: }
085:
086: double x0 = dataset.getXValue(series, item - 1);
087: double y0 = dataset.getYValue(series, item - 1);
088: if (Double.isNaN(y0) || Double.isNaN(x0)) {
089: return;
090: }
091:
092: RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
093: RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
094:
095: double transX0 = domainAxis.valueToJava2D(x0, dataArea,
096: xAxisLocation);
097: double transY0 = rangeAxis.valueToJava2D(y0, dataArea,
098: yAxisLocation);
099:
100: double transX1 = domainAxis.valueToJava2D(x1, dataArea,
101: xAxisLocation);
102: double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
103: yAxisLocation);
104:
105: // only draw if we have good values
106: if (Double.isNaN(transX0) || Double.isNaN(transY0)
107: || Double.isNaN(transX1) || Double.isNaN(transY1)) {
108: return;
109: }
110:
111: Point2D.Double point0 = new Point2D.Double();
112: Point2D.Double point1 = new Point2D.Double();
113: Point2D.Double point2 = new Point2D.Double();
114: Point2D.Double point3 = new Point2D.Double();
115:
116: if (item == 1) {
117: point0 = null;
118: } else {
119: point0.x = domainAxis.valueToJava2D(dataset.getXValue(
120: series, item - 2), dataArea, xAxisLocation);
121: point0.y = rangeAxis.valueToJava2D(dataset.getYValue(
122: series, item - 2), dataArea, yAxisLocation);
123: }
124:
125: point1.x = transX0;
126: point1.y = transY0;
127:
128: point2.x = transX1;
129: point2.y = transY1;
130:
131: if ((item + 1) == dataset.getItemCount(series)) {
132: point3 = null;
133: } else {
134: point3.x = domainAxis.valueToJava2D(dataset.getXValue(
135: series, item + 1), dataArea, xAxisLocation);
136: point3.y = rangeAxis.valueToJava2D(dataset.getYValue(
137: series, item + 1), dataArea, yAxisLocation);
138: }
139:
140: int steps = ((int) ((point2.x - point1.x) / 0.2) < 30) ? (int) ((point2.x - point1.x) / 0.2)
141: : 30;
142:
143: Point2D.Double[] points = getBezierCurve(point0, point1,
144: point2, point3, 1, steps);
145:
146: for (int i = 1; i < points.length; i++) {
147: transX0 = points[i - 1].x;
148: transY0 = points[i - 1].y;
149: transX1 = points[i].x;
150: transY1 = points[i].y;
151:
152: PlotOrientation orientation = plot.getOrientation();
153: if (orientation == PlotOrientation.HORIZONTAL) {
154: state.workingLine.setLine(transY0, transX0, transY1,
155: transX1);
156: } else if (orientation == PlotOrientation.VERTICAL) {
157: state.workingLine.setLine(transX0, transY0, transX1,
158: transY1);
159: }
160:
161: if (state.workingLine.intersects(dataArea)) {
162: drawFirstPassShape(g2, pass, series, item,
163: state.workingLine);
164: }
165: }
166: }
167:
168: protected void drawSecondaryPass(Graphics2D g2, XYPlot plot,
169: XYDataset dataset, int pass, int series, int item,
170: ValueAxis domainAxis, Rectangle2D dataArea,
171: ValueAxis rangeAxis, CrosshairState crosshairState,
172: EntityCollection entities) {
173: // super.drawSecondaryPass(g2, plot, dataset, pass, series, item,
174: // domainAxis, dataArea, rangeAxis, crosshairState, entities);
175: }
176:
177: /**
178: * Updates the control points.
179: *
180: * @param point0
181: * @param point1
182: * @param point2
183: * @param point3
184: * @param control1
185: * @param control2
186: * @param smooth
187: */
188: public static void getControlPoints(Point2D.Double point0,
189: Point2D.Double point1, Point2D.Double point2,
190: Point2D.Double point3, Point2D.Double control1,
191: Point2D.Double control2, double smooth) {
192:
193: // Reference: http://www.antigrain.com/research/bezier_interpolation/
194:
195: if (point0 == null)
196: point0 = point1; //new Point2D.Double(0, 0);
197: if (point3 == null)
198: point3 = point2; //new Point2D.Double(0, 0);
199:
200: Point2D.Double c1 = new Point2D.Double(
201: (point0.x + point1.x) / 2.0,
202: (point0.y + point1.y) / 2.0);
203: Point2D.Double c2 = new Point2D.Double(
204: (point1.x + point2.x) / 2.0,
205: (point1.y + point2.y) / 2.0);
206: Point2D.Double c3 = new Point2D.Double(
207: (point2.x + point3.x) / 2.0,
208: (point2.y + point3.y) / 2.0);
209:
210: double len1 = point1.distance(point0);
211: double len2 = point2.distance(point1);
212: double len3 = point3.distance(point2);
213:
214: double k1 = len1 / (len1 + len2);
215: double k2 = len2 / (len2 + len3);
216:
217: Point2D.Double m1 = new Point2D.Double(c1.x + (c2.x - c1.x)
218: * k1, c1.y + (c2.y - c1.y) * k1);
219: Point2D.Double m2 = new Point2D.Double(c2.x + (c3.x - c2.x)
220: * k2, c2.y + (c3.y - c2.y) * k2);
221:
222: control1.setLocation(new Point2D.Double(m1.x + (c2.x - m1.x)
223: * smooth + point1.x - m1.x, m1.y + (c2.y - m1.y)
224: * smooth + point1.y - m1.y));
225: control2.setLocation(new Point2D.Double(m2.x + (c2.x - m2.x)
226: * smooth + point2.x - m2.x, m2.y + (c2.y - m2.y)
227: * smooth + point2.y - m2.y));
228: }
229:
230: /**
231: * Returns the points for a bezier curve.
232: *
233: * @param point0
234: * @param point1
235: * @param point2
236: * @param point3
237: * @param smooth
238: * @param steps
239: *
240: * @return The curve points.
241: */
242: public static Point2D.Double[] getBezierCurve(
243: Point2D.Double point0, Point2D.Double point1,
244: Point2D.Double point2, Point2D.Double point3,
245: double smooth, int steps) {
246: Point2D.Double control1 = new Point2D.Double();
247: Point2D.Double control2 = new Point2D.Double();
248:
249: getControlPoints(point0, point1, point2, point3, control1,
250: control2, smooth);
251:
252: Point2D.Double C = new Point2D.Double(
253: 3 * (control1.x - point1.x),
254: 3 * (control1.y - point1.y));
255: Point2D.Double B = new Point2D.Double(3
256: * (control2.x - control1.x) - C.x, 3
257: * (control2.y - control1.y) - C.y);
258: Point2D.Double A = new Point2D.Double(point2.x - point1.x - C.x
259: - B.x, point2.y - point1.y - C.y - B.y);
260:
261: Point2D.Double[] res = new Point2D.Double[steps + 1];
262: double stepSize = 1.0 / steps;
263: double step = stepSize;
264:
265: res[0] = point1;
266: for (int i = 1; i < steps; i++) {
267: res[i] = new Point2D.Double(A.x * Math.pow(step, 3) + B.x
268: * Math.pow(step, 2) + C.x * step + point1.x, A.y
269: * Math.pow(step, 3) + B.y * Math.pow(step, 2) + C.y
270: * step + point1.y);
271: //System.out.println(step + " : " + res[i]);
272: step += stepSize;
273: }
274: res[steps] = point2;
275:
276: return res;
277: }
278:
279: }
|