How to make line with rounded (smooth) corners with AndroidPlot
android, androidplot
Solution
You can use Path.cubicTo() method. It draws a line using cubic spline algorithm which results in the smoothing effect you want.
Checkout the answer to a similar question here, where a guy is talking about cubic splines. There is a short algorithm showing how to calculate input parameters for `Path.cubicTo()` method. You can play with divider values to achieve required smoothness. For example, in the picture below I divided by 5 instead of 3. Hope this helps.
I have spent some time and implemented a `SplineLineAndPointFormatter` class, which does the stuff you need in androidplot library. It uses same technics. Here is how androidplot example applications looks like. You just need to use it instead of `LineAndPointFormatter`.
Here is code example and the class I wrote.
f1 = new SplineLineAndPointFormatter(color.getColor(), null,
Color.argb(60, color.getRed(), color.getGreen(), color.getBlue()), null);
plot.addSeries(series1, f1);
Here is the class doing the magic. It is based on version 0.6.1 of androidplot library.
package com.androidplot.xy;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import com.androidplot.ui.SeriesRenderer;
import com.androidplot.util.ValPixConverter;
public class SplineLineAndPointFormatter extends LineAndPointFormatter {
public SplineLineAndPointFormatter() { }
public SplineLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor) {
super(lineColor, vertexColor, fillColor, null);
}
public SplineLineAndPointFormatter(Integer lineColor, Integer vertexColor, Integer fillColor, FillDirection fillDir) {
super(lineColor, vertexColor, fillColor, null, fillDir);
}
@Override
public Class<? extends SeriesRenderer> getRendererClass() {
return SplineLineAndPointRenderer.class;
}
@Override
public SeriesRenderer getRendererInstance(XYPlot plot) {
return new SplineLineAndPointRenderer(plot);
}
public static class SplineLineAndPointRenderer extends LineAndPointRenderer<BezierLineAndPointFormatter> {
static class Point {
public float x, y, dx, dy;
public Point(PointF pf) { x = pf.x; y = pf.y; }
}
private Point prev, point, next;
private int pointsCounter;
public SplineLineAndPointRenderer(XYPlot plot) {
super(plot);
}
@Override
protected void appendToPath(Path path, final PointF thisPoint, PointF lastPoint) {
pointsCounter--;
if (point == null) {
point = new Point(thisPoint);
point.dx = ((point.x - prev.x) / 5);
point.dy = ((point.y - prev.y) / 5);
return;
} else if (next == null) {
next = new Point(thisPoint);
} else {
prev = point;
point = next;
next = new Point(thisPoint);
}
point.dx = ((next.x - prev.x) / 5);
point.dy = ((next.y - prev.y) / 5);
path.cubicTo(prev.x + prev.dx, prev.y + prev.dy, point.x - point.dx, point.y - point.dy, point.x, point.y);
if (pointsCounter == 1) { // last point
next.dx = ((next.x - point.x) / 5);
next.dy = ((next.y - point.y) / 5);
path.cubicTo(point.x + point.dx, point.y + point.dy, next.x - next.dx, next.y - next.dy, next.x, next.y);
}
}
@Override
protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
Number y = series.getY(0);
Number x = series.getX(0);
if (x == null || y == null) throw new IllegalArgumentException("no null values in xyseries permitted");
XYPlot p = getPlot();
PointF thisPoint = ValPixConverter.valToPix(x, y, plotArea,
p.getCalculatedMinX(), p.getCalculatedMaxX(), p.getCalculatedMinY(), p.getCalculatedMaxY());
prev = new Point(thisPoint);
point = next = null;
pointsCounter = series.size();
super.drawSeries(canvas, plotArea, series, formatter);
}
}
}
Problem
I have a small problem with ploting my graph. On a picture below is what I have already done. The graph should represent the actual signal strength of available Wi-Fi network(s). It's a simple `XYPlot` here data are represented with `SimpleXYSeries` (values are dynamically created). Here is a little snippet of code (only for example): ``` plot = (XYPlot) findViewById(R.id.simplexyPlot); series1 = new SimpleXYSeries(Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Link 1"); f1 = new LineAndPointFormatter(color.getColor(), null, Color.argb(60, color.getRed(), color.getGreen(), color.getBlue()), null); plot.addSeries(series1, f1); ``` The example in the picture is a dynamic simulation of dB changes. Everything works, I guess, correctly, but what I want to achieve is to have line with "rounded" corners (see the picture to see what I mean). I already tried to customize LineFormatter: ``` f1.getFillPaint().setStrokeJoin(Join.ROUND); f1.getFillPaint().setStrokeWidth(8); ``` But this didn't work as expected. Note: The Wifi Analyzer application has a similar graph and its graph has the rounded corners I want. It looks like this: