package net.osmand.plus.views;
import java.util.Map;
import net.osmand.plus.R;
import net.osmand.router.TurnType;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
public class TurnPathHelper {
//Index of processed turn
public static final int FIRST_TURN = 1;
public static final int SECOND_TURN = 2;
public static final int THIRD_TURN = 3;
private static final boolean SHOW_STEPS = true;
private static class TurnVariables {
float radEndOfArrow = 44;
float radInnerCircle = 10;
float radOuterCircle = radInnerCircle + 8;
float radBottom = radOuterCircle + 10;
float radStepInter = radOuterCircle + 6;
float radArrowTriangle1 = radOuterCircle + 7;
float widthStepIn = 8;
float widthStepInter = 6;
float widthArrow = 22;
float radArrowTriangle2;
private double dfL;
private double dfAr2;
private double dfStepInter;
private double dfAr;
private double dfOut;
private double dfStepOut;
private double dfIn;
private double minDelta;
private double rot;
private float cx;
private float cy;
private float scaleTriangle;
private TurnVariables(boolean leftSide, float turnAngle, int out, int wa, int ha, float scaleTriangle) {
this.scaleTriangle = scaleTriangle;
widthArrow = widthArrow * scaleTriangle;
radArrowTriangle2 = radArrowTriangle1 + 1 * scaleTriangle * scaleTriangle;
dfL = (leftSide ? 1 : -1) * Math.asin(widthStepIn / (2.0 * radBottom));
dfAr2 = (leftSide ? 1 : -1) * Math.asin(widthArrow / (2.0 * radArrowTriangle2));
dfStepInter = (leftSide ? 1 : -1) * Math.asin(widthStepInter / radStepInter);
dfAr = Math.asin(radBottom * Math.sin(dfL) / radArrowTriangle1);
dfOut = Math.asin(radBottom * Math.sin(dfL) / radOuterCircle);
dfStepOut = Math.asin(radStepInter * Math.sin(dfStepInter) / radOuterCircle);
dfIn = Math.asin(radBottom * Math.sin(dfL) / radInnerCircle);
minDelta = Math.abs(dfIn * 2 / Math.PI * 180) + 2;
// System.out.println("Angle " + dfL + " " + dfOut + " " + dfIn + " " + minDelta + " ");
rot = alignRotation(turnAngle, leftSide, minDelta, out) / 180 * Math.PI;
cx = wa / 2;
cy = ha / 2;
// align center
float potentialArrowEndX = (float) (Math.sin(rot) * radEndOfArrow);
float potentialArrowEndY = (float) (Math.cos(rot) * radEndOfArrow);
if (potentialArrowEndX > cx) {
cx = potentialArrowEndX;
} else if (potentialArrowEndX < -cx) {
cx = 2 * cx + potentialArrowEndX;
}
if (potentialArrowEndY > cy) {
cy = 2 * cy - potentialArrowEndY;
} else if (potentialArrowEndY < -cy) {
cy = -potentialArrowEndY;
}
}
private float getProjX(double angle, double radius) {
return getX(angle, radius) + cx;
}
private float getProjY(double angle, double radius) {
return getY(angle, radius) + cy;
}
public float getTriangle2X() {
return getProjX(rot + dfAr, radArrowTriangle1);
}
public float getTriangle1X() {
return getProjX(rot - dfAr, radArrowTriangle1);
}
public float getTriangle2Y() {
return getProjY(rot + dfAr, radArrowTriangle1);
}
public float getTriangle1Y() {
return getProjY(rot - dfAr, radArrowTriangle1);
}
public void drawTriangle(Path pathForTurn) {
// up from arc
arcLineTo(pathForTurn, rot - dfAr, cx, cy, radArrowTriangle1);
// left triangle
// arcLineTo(pathForTurn, rot - dfAr2, cx, cy, radAr2); // 1.
// arcQuadTo(pathForTurn, rot - dfAr2, radAr2, rot, radArrow, 0.9f, cx, cy); // 2.
arcQuadTo(pathForTurn, rot - dfAr, radArrowTriangle1, rot - dfAr2, radArrowTriangle2, rot, radEndOfArrow,
4.5f * scaleTriangle, cx, cy); // 3.
// arcLineTo(pathForTurn, rot, cx, cy, radArrow); // 1.
arcQuadTo(pathForTurn, rot - dfAr2, radArrowTriangle2, rot, radEndOfArrow, rot + dfAr2, radArrowTriangle2,
4.5f, cx, cy);
// right triangle
// arcLineTo(pathForTurn, rot + dfAr2, cx, cy, radAr2); // 1.
arcQuadTo(pathForTurn, rot, radEndOfArrow, rot + dfAr2, radArrowTriangle2, rot + dfAr, radArrowTriangle1,
4.5f * scaleTriangle, cx, cy);
arcLineTo(pathForTurn, rot + dfAr, cx, cy, radArrowTriangle1);
}
}
// 72x72
public static void calcTurnPath(Path pathForTurn, Path outlay, TurnType turnType,
Matrix transform, PointF center, boolean mini) {
if(turnType == null){
return;
}
pathForTurn.reset();
if(outlay != null) {
outlay.reset();
}
int ha = 72;
int wa = 72;
int lowMargin = 6;
if (TurnType.C == turnType.getValue()) {
TurnVariables tv = new TurnVariables(false, 0, 0, wa, ha, 1.5f);
pathForTurn.moveTo(wa / 2 + tv.widthStepIn / 2, ha - lowMargin);
tv.drawTriangle(pathForTurn);
pathForTurn.lineTo(wa / 2 - tv.widthStepIn / 2, ha - lowMargin);
} else if (TurnType.OFFR == turnType.getValue()){
TurnVariables tv = new TurnVariables(false, 0, 0, wa, ha, 1.5f);
float rightX = wa / 2 + tv.widthStepIn / 2;
float leftX = wa / 2 - tv.widthStepIn / 2;
int step = 7;
pathForTurn.moveTo(rightX, ha - lowMargin);
pathForTurn.rLineTo(0, -step);
pathForTurn.rLineTo(-tv.widthStepIn , 0);
pathForTurn.rLineTo(0 , step);
pathForTurn.rLineTo(tv.widthStepIn, 0);
pathForTurn.moveTo(rightX, ha - 2 * lowMargin - step);
pathForTurn.rLineTo(0, -step);
pathForTurn.rLineTo(-tv.widthStepIn , 0);
pathForTurn.rLineTo(0 , step);
pathForTurn.rLineTo(tv.widthStepIn, 0);
pathForTurn.moveTo(rightX, ha - 3 * lowMargin - 2 * step);
pathForTurn.rLineTo(0, -step);
pathForTurn.rLineTo(-tv.widthStepIn , 0);
pathForTurn.rLineTo(0 , step);
pathForTurn.rLineTo(tv.widthStepIn, 0);
pathForTurn.moveTo(rightX, ha - 4 * lowMargin - 3 * step);
tv.drawTriangle(pathForTurn);
pathForTurn.lineTo(leftX, ha - 4 * lowMargin - 3 * step);
} else if (TurnType.TR == turnType.getValue()|| TurnType.TL == turnType.getValue()) {
int b = TurnType.TR == turnType.getValue()? 1 : -1;
TurnVariables tv = new TurnVariables(b != 1, b == 1 ? 90 : -90, 0, wa, ha / 2, 1.5f);
float centerCurveX = wa / 2 + b * 4;
float centerCurveY = ha / 2;
// calculated
float h = centerCurveY - lowMargin;
float r = tv.cy - tv.widthStepIn / 2;
float centerLineX = centerCurveX - b * (r + tv.widthStepIn / 2);
RectF innerOval = new RectF(centerCurveX - r, centerCurveY - r, centerCurveX + r, centerCurveY + r);
RectF outerOval = new RectF(innerOval);
outerOval.inset(-tv.widthStepIn, -tv.widthStepIn);
pathForTurn.moveTo(centerLineX + b * tv.widthStepIn / 2, ha - lowMargin);
pathForTurn.rLineTo(0, -h);
pathForTurn.arcTo(innerOval, b == 1 ? -180 : 0, b* 90);
tv.drawTriangle(pathForTurn);
pathForTurn.arcTo(outerOval, -90, - b *90);
pathForTurn.rLineTo(0, h);
} else if (TurnType.TSLR == turnType.getValue() || TurnType.TSLL == turnType.getValue()) {
int b = TurnType.TSLR == turnType.getValue() ? 1 : -1;
TurnVariables tv = new TurnVariables(b != 1, b == 1 ? 45 : -45, 0, wa, ha, 1.5f);
tv.cx -= b * 7;
float centerBottomX = wa / 2 - b * 6;
float centerCurveY = ha / 2 + 8;
float centerCurveX = centerBottomX + b * (wa / 2);
// calculated
float rx1 = Math.abs(centerCurveX - centerBottomX) - tv.widthStepIn / 2;
float rx2 = Math.abs(centerCurveX - centerBottomX) + tv.widthStepIn / 2;
double t1 = Math.acos(Math.abs(tv.getTriangle1X() - centerCurveX) / rx1) ;
float rb1 = (float) (Math.abs(tv.getTriangle1Y() - centerCurveY) / Math.sin(t1));
float ellipseAngle1 = (float) (t1 / Math.PI * 180);
double t2 = Math.acos(Math.abs(tv.getTriangle2X() - centerCurveX) / rx2) ;
float rb2 = (float) (Math.abs(tv.getTriangle2Y() - centerCurveY) / Math.sin(t2));
float ellipseAngle2 = (float) (t2 / Math.PI * 180);
RectF innerOval = new RectF(centerCurveX - rx1, centerCurveY - rb1, centerCurveX + rx1, centerCurveY + rb1);
RectF outerOval = new RectF(centerCurveX - rx2, centerCurveY - rb2, centerCurveX + rx2, centerCurveY + rb2);
pathForTurn.moveTo(centerBottomX + b * tv.widthStepIn / 2, ha - lowMargin);
pathForTurn.arcTo(innerOval, -90 - b * 90, b * (ellipseAngle1));
tv.drawTriangle(pathForTurn);
pathForTurn.arcTo(outerOval, -90 - b * (90 - (ellipseAngle2)), -b * (ellipseAngle2));
pathForTurn.lineTo(centerBottomX - b * tv.widthStepIn / 2, ha - lowMargin);
} else if (TurnType.TSHR == turnType.getValue() || TurnType.TSHL == turnType.getValue()) {
int b = TurnType.TSHR == turnType.getValue() ? 1 : -1;
float centerCircleY = ha / 4;
float centerCircleX = wa / 2 - b * (wa / 5);
TurnVariables tv = new TurnVariables(b != 1, b == 1 ? 135 : -135, 0, wa, ha, 1.5f);
// calculated
float angle = 45;
float r = tv.widthStepIn / 2;
tv.cx = centerCircleX;
tv.cy = centerCircleY;
RectF innerOval = new RectF(centerCircleX - r, centerCircleY - r, centerCircleX + r, centerCircleY + r);
pathForTurn.moveTo(centerCircleX + b * tv.widthStepIn / 2, ha - lowMargin);
pathForTurn.lineTo(centerCircleX + b * tv.widthStepIn / 2, (float) (centerCircleY +
2 * r));
// pathForTurn.arcTo(innerOval, -90 - b * 90, b * 45);
tv.drawTriangle(pathForTurn);
// pathForTurn.lineTo(centerCircleX - b * tv.widthStepIn / 2, (float) (centerCircleY - 2 *r));
pathForTurn.arcTo(innerOval, -90 + b * angle, - b * (90 + angle));
pathForTurn.lineTo(centerCircleX - b * tv.widthStepIn / 2, ha - lowMargin);
} else if(TurnType.TU == turnType.getValue() || TurnType.TRU == turnType.getValue()) {
int b = TurnType.TU == turnType.getValue() ? -1 : 1;
float radius = 16;
float centerRadiusY = ha / 2 - 10;
float extraMarginBottom = 5;
TurnVariables tv = new TurnVariables(b != 1, 180, 0, wa, ha, 1.5f);
// calculated
float centerRadiusX = wa / 2;
tv.cx = centerRadiusX + b * radius;
tv.cy = centerRadiusY - extraMarginBottom;
lowMargin += extraMarginBottom;
tv.rot = 0;
float r = radius - tv.widthStepIn / 2;
float r2 = radius + tv.widthStepIn / 2;
RectF innerOval = new RectF(centerRadiusX - r, centerRadiusY - r, centerRadiusX + r, centerRadiusY + r);
RectF outerOval = new RectF(centerRadiusX - r2, centerRadiusY - r2, centerRadiusX + r2, centerRadiusY + r2);
pathForTurn.moveTo(centerRadiusX - b * (radius - tv.widthStepIn / 2), ha - lowMargin);
pathForTurn.lineTo(centerRadiusX - b * (radius - tv.widthStepIn / 2), centerRadiusY);
pathForTurn.arcTo(innerOval, -90 - b * 90, b * 180);
tv.drawTriangle(pathForTurn);
pathForTurn.arcTo(outerOval, -90 + b * 90, -b * 180);
pathForTurn.lineTo(centerRadiusX - b * (radius + tv.widthStepIn / 2), ha - lowMargin);
} else if (TurnType.KL == turnType.getValue() || TurnType.KR == turnType.getValue()) {
int b = TurnType.KR == turnType.getValue()? 1 : -1;
float shiftX = 8;
float firstH = 18;
float secondH = 20;
TurnVariables tv = new TurnVariables(false, 0, 0, wa, ha, 1.5f);
// calculated
tv.cx += b * shiftX;
pathForTurn.moveTo(wa / 2 + tv.widthStepIn / 2 - b * shiftX, ha - lowMargin);
pathForTurn.lineTo(wa / 2 + tv.widthStepIn / 2 - b * shiftX, ha - lowMargin - firstH);
// pathForTurn.lineTo(wa / 2 + tv.widthStepIn / 2 + b * shiftX, ha - lowMargin - firstH - secondH);
pathForTurn.cubicTo(
wa / 2 + tv.widthStepIn / 2 - b * shiftX, ha - lowMargin - firstH - secondH / 2 + b * 3,
wa / 2 + tv.widthStepIn / 2 + b * shiftX, ha - lowMargin - firstH - secondH / 2 + b * 3,
wa / 2 + tv.widthStepIn / 2 + b * shiftX, ha - lowMargin - firstH - secondH);
tv.drawTriangle(pathForTurn);
pathForTurn.lineTo(wa / 2 - tv.widthStepIn / 2 + b * shiftX, ha - lowMargin - firstH - secondH);
pathForTurn.cubicTo(
wa / 2 - tv.widthStepIn / 2 + b * shiftX, ha - lowMargin - firstH - secondH / 2 - b * 2,
wa / 2 - tv.widthStepIn / 2 - b * shiftX, ha - lowMargin - firstH - secondH / 2 - b * 2,
wa / 2 - tv.widthStepIn / 2 - b * shiftX, ha - lowMargin - firstH );
// pathForTurn.lineTo(wa / 2 - tv.widthStepIn / 2 - b * shiftX, ha - lowMargin - firstH);
pathForTurn.lineTo(wa / 2 - tv.widthStepIn / 2 - b * shiftX, ha - lowMargin);
} else if(turnType != null && turnType.isRoundAbout() ) {
int out = turnType.getExitOut();
boolean leftSide = turnType.isLeftSide();
boolean showSteps = SHOW_STEPS && !mini;
TurnVariables tv = new TurnVariables(leftSide, turnType.getTurnAngle(), out, wa, ha, 1);
if(center != null) {
center.set(tv.cx, tv.cy);
}
RectF qrOut = new RectF(tv.cx - tv.radOuterCircle, tv.cy - tv.radOuterCircle,
tv.cx + tv.radOuterCircle, tv.cy + tv.radOuterCircle);
RectF qrIn = new RectF(tv.cx - tv.radInnerCircle, tv.cy - tv.radInnerCircle, tv.cx + tv.radInnerCircle, tv.cy + tv.radInnerCircle);
if(outlay != null && !mini) {
outlay.addArc(qrOut, 0, 360);
outlay.addArc(qrIn, 0, -360);
// outlay.addOval(qrOut, Direction.CCW);
// outlay.addOval(qrIn, Direction.CW);
}
// move to bottom ring
pathForTurn.moveTo(tv.getProjX(tv.dfOut, tv.radOuterCircle), tv.getProjY(tv.dfOut, tv.radOuterCircle));
if (out <= 1) {
showSteps = false;
}
if (showSteps && outlay != null) {
double totalStepInter = (out - 1) * tv.dfStepOut;
double st = (tv.rot - 2 * tv.dfOut - totalStepInter) / out;
if ((tv.rot > 0) != (st > 0)) {
showSteps = false;
}
if (Math.abs(st) < Math.PI / 60) {
showSteps = false;
}
// double st = (rot - 2 * dfOut ) / (2 * out - 1);
// dfStepOut = st;
if (showSteps) {
outlay.moveTo(tv.getProjX(tv.dfOut, tv.radOuterCircle), tv.getProjY(tv.dfOut, tv.radOuterCircle));
for (int i = 0; i < out - 1; i++) {
outlay.arcTo(qrOut, startArcAngle(tv.dfOut + i * (st + tv.dfStepOut)), sweepArcAngle(st));
arcLineTo(outlay,
tv.dfOut + (i + 1) * (st + tv.dfStepOut) - tv.dfStepOut / 2 - tv.dfStepInter / 2,
tv.cx, tv.cy, tv.radStepInter);
arcLineTo(outlay, tv.dfOut + (i + 1) * (st + tv.dfStepOut) - tv.dfStepOut / 2 + tv.dfStepInter / 2,
tv.cx, tv.cy, tv.radStepInter);
arcLineTo(outlay, tv.dfOut + (i + 1) * (st + tv.dfStepOut), tv.cx, tv.cy, tv.radOuterCircle);
// pathForTurn.arcTo(qr1, startArcAngle(dfOut), sweepArcAngle(rot - dfOut - dfOut));
}
outlay.arcTo(qrOut, startArcAngle(tv.rot - tv.dfOut - st), sweepArcAngle(st));
// swipe back
arcLineTo(outlay, tv.rot - tv.dfIn, tv.cx, tv.cy, tv.radInnerCircle);
outlay.arcTo(qrIn, startArcAngle(tv.rot - tv.dfIn), -sweepArcAngle(tv.rot - tv.dfIn - tv.dfIn));
}
}
// if(!showSteps) {
// // arc
// pathForTurn.arcTo(qrOut, startArcAngle(dfOut), sweepArcAngle(rot - dfOut - dfOut));
// }
pathForTurn.arcTo(qrOut, startArcAngle(tv.dfOut), sweepArcAngle(tv.rot - tv.dfOut - tv.dfOut));
tv.drawTriangle(pathForTurn);
// down to arc
arcLineTo(pathForTurn, tv.rot + tv.dfIn, tv.cx, tv.cy, tv.radInnerCircle);
// arc
pathForTurn.arcTo(qrIn, startArcAngle(tv.rot + tv.dfIn), sweepArcAngle(-tv.rot - tv.dfIn - tv.dfIn));
// down
arcLineTo(pathForTurn, -tv.dfL, tv.cx, tv.cy, tv.radBottom);
// left
arcLineTo(pathForTurn, tv.dfL, tv.cx, tv.cy, tv.radBottom);
}
pathForTurn.close();
if(transform != null){
pathForTurn.transform(transform);
}
}
private static float alignRotation(float t, boolean leftSide, double minDelta, int out) {
// t between ]-180, 180]
while(t > 180) {
t -= 360;
}
while(t <= -180) {
t += 360;
}
// rot left - ] 0, 360], right ] -360,0]
float rot = leftSide ? (t + 180) : (t - 180) ;
if(rot == 0) {
rot = leftSide ? 360 : -360;
}
float delta = (float) minDelta;
if(rot > 360 - delta && rot <= 360) {
rot = 360 - delta;
if(out < 2) {
rot = delta;
}
} else if (rot < -360 + delta && rot >= -360) {
rot = -360 + delta;
if(out < 2) {
rot = -delta;
}
} else if (rot >= 0 && rot < delta) {
rot = delta;
if(out > 2) {
rot = 360 - delta;
}
} else if (rot <= 0 && rot > -delta) {
rot = -delta;
if(out > 2) {
rot = -360 + delta;
}
}
return rot;
}
private static void arcLineTo(Path pathForTurn, double angle, float cx, float cy, float radius) {
pathForTurn.lineTo(getProjX(angle, cx, cy, radius), getProjY(angle, cx, cy, radius));
}
private static void arcQuadTo(Path pathForTurn, double angle, float radius, double angle2, float radius2,
float proc, float cx, float cy) {
float X = getProjX(angle, cx, cy, radius);
float Y = getProjY(angle, cx, cy, radius);
float X2 = getProjX(angle2, cx, cy, radius2);
float Y2 = getProjY(angle2, cx, cy, radius2);
pathForTurn.quadTo(X, Y, X2 * proc + X * (1 - proc), Y2 * proc + Y * (1 - proc));
}
private static void arcQuadTo(Path pathForTurn, double angle0, float radius0, double angle, float radius, double angle2, float radius2,
float dl, float cx, float cy) {
float X0 = getProjX(angle0, cx, cy, radius0);
float Y0 = getProjY(angle0, cx, cy, radius0);
float X = getProjX(angle, cx, cy, radius);
float Y = getProjY(angle, cx, cy, radius);
float X2 = getProjX(angle2, cx, cy, radius2);
float Y2 = getProjY(angle2, cx, cy, radius2);
float l2 = (float) Math.sqrt((X-X2)*(X-X2) + (Y-Y2)*(Y-Y2));
float l0 = (float) Math.sqrt((X-X0)*(X-X0) + (Y-Y0)*(Y-Y0));
float proc2 = (float) (dl / l2);
float proc = (float) (dl / l0);
pathForTurn.lineTo(X0 * proc + X * (1 - proc), Y0 * proc + Y * (1 - proc));
pathForTurn.quadTo(X, Y, X2 * proc2 + X * (1 - proc2), Y2 * proc2 + Y * (1 - proc2));
}
// angle - bottom is zero, right is -90, left is 90
private static float getX(double angle, double radius) {
return (float) (Math.cos(angle + Math.PI / 2) * radius);
}
private static float getY(double angle, double radius) {
return (float) (Math.sin(angle + Math.PI / 2) * radius);
}
private static float getProjX(double angle, float cx, float cy, double radius) {
return getX(angle, radius) + cx;
}
private static float getProjY(double angle, float cx, float cy, double radius) {
return getY(angle, radius) + cy;
}
private static float startArcAngle(double i) {
return (float) (i * 180 / Math.PI + 90);
}
private static float sweepArcAngle(double d) {
return (float) (d * 180 / Math.PI);
}
public static class RouteDrawable extends Drawable {
Paint paintRouteDirection;
Paint paintRouteDirectionOutlay;
Path p = new Path();
Path dp = new Path();
Path pOutlay = new Path();
Path dpOutlay = new Path();
private boolean mini;
public RouteDrawable(Resources resources, boolean mini){
this.mini = mini;
paintRouteDirection = new Paint();
paintRouteDirection.setStyle(Style.FILL_AND_STROKE);
paintRouteDirection.setColor(resources.getColor(R.color.nav_arrow_distant));
paintRouteDirection.setAntiAlias(true);
paintRouteDirectionOutlay = new Paint();
paintRouteDirectionOutlay.setStyle(Style.STROKE);
paintRouteDirectionOutlay.setColor(Color.BLACK);
paintRouteDirectionOutlay.setAntiAlias(true);
TurnPathHelper.calcTurnPath(dp, dpOutlay, TurnType.straight(), null, null, mini);
}
@Override
protected void onBoundsChange(Rect bounds) {
Matrix m = new Matrix();
m.setScale(bounds.width() / 72f, bounds.height() / 72f);
p.transform(m, dp);
pOutlay.transform(m, dpOutlay);
}
public void setRouteType(TurnType t){
TurnPathHelper.calcTurnPath(p, pOutlay, t, null, null, mini);
onBoundsChange(getBounds());
}
@Override
public void draw(Canvas canvas) {
canvas.drawPath(dpOutlay, paintRouteDirectionOutlay);
canvas.drawPath(dp, paintRouteDirection);
}
@Override
public int getOpacity() {
return 0;
}
@Override
public void setAlpha(int alpha) {
paintRouteDirection.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paintRouteDirection.setColorFilter(cf);
}
}
public static class TurnResource {
boolean flip;
int resourceId;
public TurnResource(){}
public TurnResource(int resourceId, boolean value) {
this.resourceId = resourceId;
this.flip = value;
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return resourceId * (flip ? -1 : 1);
}
}
private static TurnResource getTallArrow(int tt, boolean nooverlap){
TurnResource result = new TurnResource();
switch (tt){
case TurnType.C:
result.resourceId = R.drawable.map_turn_forward_small;
break;
case TurnType.TR:
case TurnType.TL:
result.resourceId = nooverlap ? R.drawable.map_turn_right_small : R.drawable.map_turn_right2_small;
break;
case TurnType.KR:
case TurnType.KL:
result.resourceId = R.drawable.map_turn_keep_right_small;
break;
case TurnType.TSLR:
case TurnType.TSLL:
result.resourceId = R.drawable.map_turn_slight_right_small;
break;
case TurnType.TSHR:
case TurnType.TSHL:
result.resourceId = R.drawable.map_turn_sharp_right_small;
break;
case TurnType.TRU:
case TurnType.TU:
result.resourceId = R.drawable.map_turn_uturn_right_small;
break;
default:
result.resourceId = R.drawable.map_turn_forward_small;
break;
}
if(tt == TurnType.TL || tt == TurnType.KL || tt == TurnType.TSLL
|| tt == TurnType.TSHL || tt == TurnType.TU){
result.flip = true;
}
return result;
}
private static TurnResource getShortArrow(int tt){
TurnResource result = new TurnResource();
switch (tt) {
case TurnType.C:
result.resourceId = R.drawable.map_turn_forward_small;
break;
case TurnType.TR:
case TurnType.TL:
result.resourceId = R.drawable.map_turn_forward_right_turn_small;
break;
case TurnType.KR:
case TurnType.KL:
result.resourceId = R.drawable.map_turn_forward_keep_right_small;
break;
case TurnType.TSLR:
case TurnType.TSLL:
result.resourceId = R.drawable.map_turn_forward_slight_right_turn_small;
break;
case TurnType.TSHR:
case TurnType.TSHL:
result.resourceId = R.drawable.map_turn_forward_turn_sharp_small;
break;
case TurnType.TRU:
case TurnType.TU:
result.resourceId = R.drawable.map_turn_forward_uturn_right_small;
break;
default:
result.resourceId = R.drawable.map_turn_forward_small;
break;
}
if(tt == TurnType.TL || tt == TurnType.KL || tt == TurnType.TSLL
|| tt == TurnType.TSHL || tt == TurnType.TU){
result.flip = true;
}
return result;
}
public static Bitmap getBitmapFromTurnType(Resources res, Map<TurnResource, Bitmap> cache, int firstTurn,
int secondTurn, int thirdTurn, int turnIndex, float coef, boolean leftSide) {
int firstTurnType = TurnType.valueOf(firstTurn, leftSide).getValue();
int secondTurnType = TurnType.valueOf(secondTurn, leftSide).getValue();
int thirdTurnType = TurnType.valueOf(thirdTurn, leftSide).getValue();
TurnResource turnResource = null;
if (turnIndex == FIRST_TURN) {
if (secondTurnType == 0) {
turnResource = getTallArrow(firstTurnType, true);
} else if (secondTurnType == TurnType.C || thirdTurnType == TurnType.C) {
turnResource = getShortArrow(firstTurnType);
} else {
if (firstTurnType == TurnType.TU || firstTurnType == TurnType.TRU) {
turnResource = getShortArrow(firstTurnType);
} else {
turnResource = getTallArrow(firstTurnType, false);
}
}
} else if (turnIndex == SECOND_TURN) {
if (TurnType.isLeftTurn(firstTurnType) && TurnType.isLeftTurn(secondTurnType)) {
turnResource = null;
} else if (TurnType.isRightTurn(firstTurnType) && TurnType.isRightTurn(secondTurnType)) {
turnResource = null;
} else if (firstTurnType == TurnType.C || thirdTurnType == TurnType.C) {
// get the small one
turnResource = getShortArrow(secondTurnType);
} else {
turnResource = getTallArrow(secondTurnType, false);
}
} else if (turnIndex == THIRD_TURN) {
if ((TurnType.isLeftTurn(firstTurnType) || TurnType.isLeftTurn(secondTurnType)) && TurnType.isLeftTurn(thirdTurnType)) {
turnResource = null;
} else if ((TurnType.isRightTurn(firstTurnType) || TurnType.isRightTurn(secondTurnType)) && TurnType.isRightTurn(thirdTurnType)) {
turnResource = null;
} else {
turnResource = getShortArrow(thirdTurnType);
}
}
if (turnResource == null) {
return null;
}
Bitmap b = cache.get(turnResource);
if (b == null) {
b = turnResource.flip ? getFlippedBitmap(res, turnResource.resourceId) : BitmapFactory.decodeResource(res,
turnResource.resourceId);
cache.put(turnResource, b);
}
// Maybe redundant scaling
/*
* float bRatio = (float)b.getWidth() / (float)b.getHeight(); float s = 72f * coef; int wq = Math.round(s /
* bRatio); int hq = Math.round(s); b = Bitmap.createScaledBitmap(b, wq, hq, false);
*/
return b;
}
public static Bitmap getFlippedBitmap(Resources res, int resId){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
//Below line is necessary to fill in opt.outWidth, opt.outHeight
Bitmap b = BitmapFactory.decodeResource(res, resId, opt);
b = Bitmap.createBitmap(opt.outWidth, opt.outHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
Matrix flipHorizontalMatrix = new Matrix();
flipHorizontalMatrix.setScale(-1, 1);
flipHorizontalMatrix.postTranslate(b.getWidth(), 0);
Bitmap bb = BitmapFactory.decodeResource(res, resId);
canvas.drawBitmap(bb, flipHorizontalMatrix, null);
return b;
}
}