diff --git a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java index 97e921a3d4..7704a2685d 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/MapUtils.java @@ -747,7 +747,9 @@ public class MapUtils { return Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY)); } - + public static double getSqrtDistance(float startX, float startY, float endX, float endY) { + return Math.sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY)); + } } diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index e02a9d13f8..dd8ba55b8a 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -14,6 +14,7 @@ import net.osmand.data.RotatedTileBox; import net.osmand.plus.track.GradientScaleType; import net.osmand.plus.views.layers.geometry.GpxGeometryWay; import net.osmand.util.Algorithms; +import net.osmand.util.MapUtils; import java.util.ArrayList; import java.util.List; @@ -118,9 +119,10 @@ public class Renderable { updateLocalPaint(p); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); if (scaleType != null) { - drawGradient(points, p, canvas, tileBox); + drawSolid(points, borderPaint, canvas, tileBox); + drawGradient(points, paint, canvas, tileBox); } else { - drawSolid(getPointsForDrawing(), p, canvas, tileBox); + drawSolid(getPointsForDrawing(), paint, canvas, tileBox); } canvas.rotate(tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); } @@ -166,7 +168,7 @@ public class Renderable { float lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon); float lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon); if (!path.isEmpty()) { - canvas.drawPath(path, paint); + canvas.drawPath(path, p); } path.reset(); path.moveTo(lastX, lastY); @@ -180,58 +182,99 @@ public class Renderable { lastPt = pt; } if (!path.isEmpty()) { - canvas.drawPath(path, paint); + canvas.drawPath(path, p); } } protected void drawGradient(List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { QuadRect tileBounds = tileBox.getLatLonBounds(); - Path currentPath = new Path(); - Path nextPath = new Path(); - Paint paint = new Paint(this.paint); + Path path = new Path(); + boolean recalculateLastXY = true; + WptPt lastPt = pts.get(0); - WptPt prevWpt = pts.get(0); - WptPt currWpt = pts.get(1); - - PointF prevXY = new PointF(); - PointF currXY = new PointF(); - PointF nextXY = new PointF(); - - boolean currLineVisible = arePointsInsideTile(prevWpt, currWpt, tileBounds); - boolean nextLineVisible; - - if (currLineVisible) { - pixXYFromWptPt(tileBox, prevXY, prevWpt); - pixXYFromWptPt(tileBox, currXY, currWpt); - canvas.drawPath(pathFromStartEnd(currentPath, prevXY, currXY), borderPaint); - } + List gradientPoints = new ArrayList<>(); + List gradientColors = new ArrayList<>(); + float gradientAngle = 0; for (int i = 1; i < pts.size(); i++) { - currWpt = pts.get(i); - WptPt nextWpt = i + 1 == pts.size() ? null : pts.get(i + 1); + WptPt pt = pts.get(i); + WptPt nextPt = i + 1 < pts.size() ? pts.get(i + 1) : null; + float nextX = nextPt == null ? 0 : tileBox.getPixXFromLatLon(nextPt.lat, nextPt.lon); + float nextY = nextPt == null ? 0 : tileBox.getPixYFromLatLon(nextPt.lat, nextPt.lon); + float lastX = 0; + float lastY = 0; + if (arePointsInsideTile(pt, lastPt, tileBounds)) { + if (recalculateLastXY) { + recalculateLastXY = false; + lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon); + lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon); + if (!path.isEmpty()) { + p.setShader(createGradient(gradientPoints, gradientColors)); + canvas.drawPath(path, p); + } + path.reset(); + path.moveTo(lastX, lastY); - nextLineVisible = arePointsInsideTile(currWpt, nextWpt, tileBounds); - if (nextWpt != null && nextLineVisible) { - pixXYFromWptPt(tileBox, currXY, currWpt); - pixXYFromWptPt(tileBox, nextXY, nextWpt); - canvas.drawPath(pathFromStartEnd(nextPath, currXY, nextXY), borderPaint); + gradientPoints.clear(); + gradientColors.clear(); + gradientPoints.add(new PointF(lastX, lastY)); + gradientColors.add(lastPt.getColor(scaleType.toColorizationType())); + } + float x = tileBox.getPixXFromLatLon(pt.lat, pt.lon); + float y = tileBox.getPixYFromLatLon(pt.lat, pt.lon); + path.lineTo(x, y); + gradientPoints.add(new PointF(x, y)); + gradientColors.add(pt.getColor(scaleType.toColorizationType())); + + if (gradientColors.size() == 2) { + gradientAngle = calculateAngle(lastX, lastY, x, y); + } + if (nextPt != null) { + float nextAngle = calculateAngle(x, y, nextX, nextY); + if (Math.abs(nextAngle - gradientAngle) > 20) { + recalculateLastXY = true; + } + } + } else { + recalculateLastXY = true; } - - if (currLineVisible) { - int prevColor = prevWpt.getColor(scaleType.toColorizationType()); - int currentColor = currWpt.getColor(scaleType.toColorizationType()); - LinearGradient gradient = new LinearGradient(prevXY.x, prevXY.y, currXY.x, currXY.y, - prevColor, currentColor, Shader.TileMode.CLAMP); - paint.setShader(gradient); - canvas.drawPath(currentPath, paint); - } - - prevWpt = currWpt; - currentPath.set(nextPath); - prevXY.set(currXY); - currXY.set(nextXY); - currLineVisible = nextLineVisible; + lastPt = pt; } + if (!path.isEmpty()) { + p.setShader(createGradient(gradientPoints, gradientColors)); + canvas.drawPath(path, p); + } + } + + private LinearGradient createGradient(List gradientPoints, List gradientColors) { + float gradientLength = 0; + List pointsLength = new ArrayList<>(gradientPoints.size() - 1); + for (int i = 1; i < gradientPoints.size(); i++) { + PointF start = gradientPoints.get(i - 1); + PointF end = gradientPoints.get(i); + pointsLength.add((float) MapUtils.getSqrtDistance(start.x, start.y, end.x, end.y)); + gradientLength += pointsLength.get(i - 1); + } + + float[] positions = new float[gradientPoints.size()]; + positions[0] = 0; + for (int i = 1; i < gradientPoints.size(); i++) { + positions[i] = positions[i - 1] + pointsLength.get(i - 1) / gradientLength; + } + + int[] colors = new int[gradientColors.size()]; + for (int i = 0; i < gradientColors.size(); i++) { + colors[i] = gradientColors.get(i); + } + + PointF gradientStart = gradientPoints.get(0); + PointF gradientEnd = gradientPoints.get(gradientPoints.size() - 1); + return new LinearGradient(gradientStart.x, gradientStart.y, gradientEnd.x, gradientEnd.y, + colors, positions, Shader.TileMode.CLAMP); + } + + private float calculateAngle(float startX, float startY, float endX, float endY) { + return (float) Math.abs(Math.toDegrees(Math.atan2(endY - startY, endX - startX))); } protected boolean arePointsInsideTile(WptPt first, WptPt second, QuadRect tileBounds) { @@ -241,19 +284,6 @@ public class Renderable { return Math.min(first.lon, second.lon) < tileBounds.right && Math.max(first.lon, second.lon) > tileBounds.left && Math.min(first.lat, second.lat) < tileBounds.top && Math.max(first.lat, second.lat) > tileBounds.bottom; } - - protected PointF pixXYFromWptPt(RotatedTileBox tileBox, PointF pointF, WptPt wptPt) { - pointF.x = tileBox.getPixXFromLatLon(wptPt.lat, wptPt.lon); - pointF.y = tileBox.getPixYFromLatLon(wptPt.lat, wptPt.lon); - return pointF; - } - - protected Path pathFromStartEnd(Path path, PointF start, PointF end) { - path.reset(); - path.moveTo(start.x, start.y); - path.lineTo(end.x, end.y); - return path; - } } public static class StandardTrack extends RenderableSegment {