package net.osmand.plus.views;
import android.graphics.Canvas;
import android.graphics.Paint;
import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.GPXUtilities.WptPt;
import java.util.ArrayList;
import java.util.List;
public class Renderable {
public static abstract class RenderableSegment {
public List<WptPt> points = null; // Original list of points
protected List<WptPt> culled = new ArrayList<>(); // Reduced/resampled list of points
protected int pointSize;
protected double segmentSize;
protected QuadRect trackBounds;
protected double zoom = -1;
protected AsynchronousResampler culler = null; // The currently active resampler
protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use
public RenderableSegment(List <WptPt> points, double segmentSize) {
this.points = points;
calculateBounds(points);
this.segmentSize = segmentSize;
}
protected void updateLocalPaint(Paint p) {
if (paint == null) {
paint = new Paint(p);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStyle(Paint.Style.FILL);
}
paint.setColor(p.getColor());
paint.setStrokeWidth(p.getStrokeWidth());
}
protected abstract void startCuller(double newZoom);
protected void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {}
public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) { // is visible?
startCuller(zoom);
drawSingleSegment(zoom, p, canvas, tileBox);
}
}
private void calculateBounds(List<WptPt> pts) {
trackBounds = new QuadRect(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
updateBounds(pts, 0);
}
protected void updateBounds(List<WptPt> pts, int startIndex) {
pointSize = pts.size();
for (int i = startIndex; i < pointSize; i++) {
WptPt pt = pts.get(i);
trackBounds.right = Math.max(trackBounds.right, pt.lon);
trackBounds.left = Math.min(trackBounds.left, pt.lon);
trackBounds.top = Math.max(trackBounds.top, pt.lat);
trackBounds.bottom = Math.min(trackBounds.bottom, pt.lat);
}
}
public void setRDP(List<WptPt> cull) {
culled = cull;
}
protected void draw(List<WptPt> pts, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (pts.size() > 1) {
updateLocalPaint(p);
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
QuadRect tileBounds = tileBox.getLatLonBounds();
WptPt lastPt = pts.get(0);
float lastx = 0;
float lasty = 0;
boolean reCalculateLastXY = true;
int size = pts.size();
for (int i = 1; i < size; i++) {
WptPt pt = pts.get(i);
if (Math.min(pt.lon, lastPt.lon) < tileBounds.right && Math.max(pt.lon, lastPt.lon) > tileBounds.left
&& Math.min(pt.lat, lastPt.lat) < tileBounds.top && Math.max(pt.lat, lastPt.lat) > tileBounds.bottom) {
if (reCalculateLastXY) {
lastx = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon);
lasty = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon);
reCalculateLastXY = false;
}
float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon);
float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon);
canvas.drawLine(lastx, lasty, x, y, paint);
lastx = x;
lasty = y;
} else {
reCalculateLastXY = true;
}
lastPt = pt;
}
canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
}
}
}
public static class StandardTrack extends RenderableSegment {
public StandardTrack(List<WptPt> pt, double base) {
super(pt, base);
}
@Override public void startCuller(double newZoom) {
if (zoom != newZoom) {
if (culler != null) {
culler.cancel(true);
}
if (zoom < newZoom) { // if line would look worse (we're zooming in) then...
culled.clear(); // use full-resolution until re-cull complete
}
zoom = newZoom;
double cullDistance = Math.pow(2.0, segmentSize - zoom); // segmentSize == epsilon
culler = new AsynchronousResampler.RamerDouglasPeucer(this, cullDistance);
culler.execute("");
}
}
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
draw(culled.isEmpty() ? points : culled, p, canvas, tileBox);
}
}
public static class CurrentTrack extends RenderableSegment {
public CurrentTrack(List<WptPt> pt) {
super(pt, 0);
}
@Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
if (points.size() != pointSize) {
updateBounds(points, pointSize);
}
drawSingleSegment(zoom, p, canvas, tileBox);
}
@Override protected void startCuller(double newZoom) {}
@Override public void drawSingleSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) {
draw(points, p, canvas, tileBox);
}
}
}