Yes. You're right. The line looks smooth with 200 points. Thanks for you input.
I tried the control points formula from the Image_Graph library as well. It produces a slightly curvier line. I'd say not much difference, really. I wanted to try the Image_Graph library to see how well the smooth line is with 200 points. However, I couldn't get the Apache and PHP set up on my Windows and Mac machines.
Code: Select all
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.FileReader;
import java.text.SimpleDateFormat;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SegmentedTimeline;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RefineryUtilities;
import org.jfree.ui.StrokeSample;
import com.tictactec.ta.lib.Core;
import com.tictactec.ta.lib.MInteger;
class SimpleChart extends ApplicationFrame {
public SimpleChart(String s) {
super(s);
JFreeChart jfreechart = createChart();
ChartPanel chartpanel = new ChartPanel(jfreechart);
chartpanel.setPreferredSize(new Dimension(500, 270));
setContentPane(chartpanel);
}
public static void getControlPoints(
Point2D.Double point0,
Point2D.Double point1,
Point2D.Double point2,
Point2D.Double point3,
Point2D.Double control1,
Point2D.Double control2,
double smooth) {
// Reference: http://www.antigrain.com/research/bezier_interpolation/
if (point0 == null) point0 = point1; //new Point2D.Double(0, 0);
if (point3 == null) point3 = point2; //new Point2D.Double(0, 0);
Point2D.Double c1 = new Point2D.Double(
(point0.x + point1.x) / 2.0, (point0.y + point1.y) / 2.0);
Point2D.Double c2 = new Point2D.Double(
(point1.x + point2.x) / 2.0, (point1.y + point2.y) / 2.0);
Point2D.Double c3 = new Point2D.Double(
(point2.x + point3.x) / 2.0, (point2.y + point3.y) / 2.0);
double len1 = point1.distance(point0);
double len2 = point2.distance(point1);
double len3 = point3.distance(point2);
double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);
Point2D.Double m1 = new Point2D.Double(
c1.x + (c2.x - c1.x) * k1, c1.y + (c2.y - c1.y) * k1);
Point2D.Double m2 = new Point2D.Double(
c2.x + (c3.x - c2.x) * k2, c2.y + (c3.y - c2.y) * k2);
control1.setLocation(new Point2D.Double(
m1.x + (c2.x - m1.x) * smooth + point1.x - m1.x,
m1.y + (c2.y - m1.y) * smooth + point1.y - m1.y));
control2.setLocation(new Point2D.Double(
m2.x + (c2.x - m2.x) * smooth + point2.x - m2.x,
m2.y + (c2.y - m2.y) * smooth + point2.y - m2.y));
}
public static Point2D.Double[] getBezierCurve(
Point2D.Double point0,
Point2D.Double point1,
Point2D.Double point2,
Point2D.Double point3,
double smooth,
int steps) {
Point2D.Double control1 = new Point2D.Double();
Point2D.Double control2 = new Point2D.Double();
getControlPoints(point0, point1, point2, point3, control1, control2, smooth);
Point2D.Double C = new Point2D.Double(
3 * (control1.x - point1.x), 3 * (control1.y - point1.y));
Point2D.Double B = new Point2D.Double(
3 * (control2.x - control1.x) - C.x, 3 * (control2.y - control1.y) - C.y);
Point2D.Double A = new Point2D.Double(
point2.x - point1.x - C.x - B.x, point2.y - point1.y - C.y - B.y);
Point2D.Double[] res = new Point2D.Double[steps + 1];
double stepSize = 1.0 / steps;
double step = stepSize;
res[0] = point1;
for (int i = 1; i < steps; i++) {
res[i] = new Point2D.Double(
A.x * Math.pow(step, 3) + B.x * Math.pow(step, 2) + C.x * step + point1.x,
A.y * Math.pow(step, 3) + B.y * Math.pow(step, 2) + C.y * step + point1.y);
System.out.println(step + " : " + res[i]);
step += stepSize;
}
res[steps] = point2;
return res;
}
private static XYDataset createDataset()
{
TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
TimeSeries timeseries = new TimeSeries("Price", org.jfree.data.time.Day.class);
TimeSeries timeseries1 = new TimeSeries("Price", org.jfree.data.time.Day.class);
float[] data = new float[200];
Day[] days = new Day[200];
int idx = 0;
int max = 20;
try {
BufferedReader reader = new BufferedReader(new FileReader("../fiex/data/PLUS-CA"));
String line = null;
SimpleDateFormat format1 = new SimpleDateFormat("yyyyMMdd");
while ((line = reader.readLine()) != null) {
String fields[] = line.split(",");
days[idx] = new Day(format1.parse(fields[2]));
//timeseries.add(new Day(format1.parse(fields[2])), 1 + Math.log10(Float.parseFloat(fields[6])));
timeseries.add(new Day(format1.parse(fields[2])), Float.parseFloat(fields[6]));
// data[idx++] = (float) (1.0D + Math.log10(Float.parseFloat(fields[6])));
data[idx++] = Float.parseFloat(fields[6]);
if (idx == max) break;
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
Core core = new Core();
MInteger m1 = new MInteger(), m2 = new MInteger();
double[] out = new double[200];
core.sma(0, idx-1, data, 7, m1, m2, out);
for (int ix = 0; ix < idx; ix++) {
// System.out.println(out[ix]);
if (days[ix+6] == null) break;
timeseries1.add(days[ix+6], out[ix]);
}
//timeseriescollection.addSeries(timeseries);
timeseriescollection.addSeries(timeseries1);
return timeseriescollection;
}
private static JFreeChart createChart() {
XYDataset xydataset = createDataset();
JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("", "Date", "Price", xydataset, true, true, false);
XYPlot xyplot = jfreechart.getXYPlot();
((DateAxis) xyplot.getDomainAxis()).setTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
NumberAxis numberaxis = (NumberAxis)xyplot.getRangeAxis();
numberaxis.setAutoRangeIncludesZero(false);
// jfreechart.setAntiAlias(true);
xyplot.setRenderer(new XYLineAndShapeRenderer() {
protected void drawPrimaryLine(XYItemRendererState state,
Graphics2D g2,
XYPlot plot,
XYDataset dataset,
int pass,
int series,
int item,
ValueAxis domainAxis,
ValueAxis rangeAxis,
Rectangle2D dataArea) {
if (item == 0) {
return;
}
// get the data point...
double x1 = dataset.getXValue(series, item);
double y1 = dataset.getYValue(series, item);
if (Double.isNaN(y1) || Double.isNaN(x1)) {
return;
}
double x0 = dataset.getXValue(series, item - 1);
double y0 = dataset.getYValue(series, item - 1);
if (Double.isNaN(y0) || Double.isNaN(x0)) {
return;
}
RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);
double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
// only draw if we have good values
if (Double.isNaN(transX0) || Double.isNaN(transY0)
|| Double.isNaN(transX1) || Double.isNaN(transY1)) {
return;
}
Point2D.Double point0 = new Point2D.Double();
Point2D.Double point1 = new Point2D.Double();
Point2D.Double point2 = new Point2D.Double();
Point2D.Double point3 = new Point2D.Double();
if (item == 1) {
point0 = null;
} else {
point0.x = domainAxis.valueToJava2D(dataset.getXValue(series, item - 2), dataArea, xAxisLocation);
point0.y = rangeAxis.valueToJava2D(dataset.getYValue(series, item - 2), dataArea, yAxisLocation);
}
point1.x = transX0;
point1.y = transY0;
point2.x = transX1;
point2.y = transY1;
if ((item + 1) == dataset.getItemCount(series)) {
point3 = null;
} else {
point3.x = domainAxis.valueToJava2D(dataset.getXValue(series, item + 1), dataArea, xAxisLocation);
point3.y = rangeAxis.valueToJava2D(dataset.getYValue(series, item + 1), dataArea, yAxisLocation);
}
int steps = ((int) ((point2.x - point1.x) / 0.2) < 30) ? (int) ((point2.x - point1.x) / 0.2) : 30;
Point2D.Double[] points = getBezierCurve(point0, point1, point2, point3, 1, steps);
for (int i = 1; i < points.length; i++) {
transX0 = points[i - 1].x;
transY0 = points[i - 1].y;
transX1 = points[i].x;
transY1 = points[i].y;
PlotOrientation orientation = plot.getOrientation();
if (orientation == PlotOrientation.HORIZONTAL) {
state.workingLine.setLine(transY0, transX0, transY1, transX1);
}
else if (orientation == PlotOrientation.VERTICAL) {
state.workingLine.setLine(transX0, transY0, transX1, transY1);
}
if (state.workingLine.intersects(dataArea)) {
drawFirstPassShape(g2, pass, series, item, state.workingLine);
}
}
}
protected void drawSecondaryPass(Graphics2D g2, XYPlot plot,
XYDataset dataset,
int pass, int series, int item,
ValueAxis domainAxis,
Rectangle2D dataArea,
ValueAxis rangeAxis,
CrosshairState crosshairState,
EntityCollection entities) {
//super.drawSecondaryPass(g2, plot, dataset, pass, series, item, domainAxis, dataArea, rangeAxis, crosshairState, entities);
}
});
xyplot.getRenderer().setStroke(new BasicStroke(1.1f));
System.out.println(jfreechart.getAntiAlias());
return jfreechart;
}
public static JPanel createDemoPanel() {
JFreeChart jfreechart = createChart();
return new ChartPanel(jfreechart);
}
public static void main(String args[]) {
SimpleChart chart = new SimpleChart("Simple Chart");
chart.pack();
RefineryUtilities.centerFrameOnScreen(chart);
chart.setVisible(true);
}
}