/** * This is where the hard-to-parse paths are handled. Uppercase rules are absolute positions, * lowercase are relative. Types of path rules: * * <p> * * <ol> * <li>M/m - (x y)+ - Move to (without drawing) * <li>Z/z - (no params) - Close path (back to starting point) * <li>L/l - (x y)+ - Line to * <li>H/h - x+ - Horizontal ine to * <li>V/v - y+ - Vertical line to * <li>C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to * <li>S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from * previous C/S is the x1, y1 of this bezier) * <li>Q/q - (x1 y1 x y)+ - Quadratic bezier to * <li>T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" * of last one w.r.t. to current point) * </ol> * * <p>Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, * (ie. begin with a - sign) * * @param s the path string from the XML */ private static Path doPath(String s) { int n = s.length(); ParserHelper ph = new ParserHelper(s, 0); ph.skipWhitespace(); Path p = new Path(); float lastX = 0; float lastY = 0; float lastX1 = 0; float lastY1 = 0; float subPathStartX = 0; float subPathStartY = 0; char prevCmd = 0; while (ph.pos < n) { char cmd = s.charAt(ph.pos); switch (cmd) { case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (prevCmd == 'm' || prevCmd == 'M') { cmd = (char) (((int) prevCmd) - 1); break; } else if (prevCmd == 'c' || prevCmd == 'C') { cmd = prevCmd; break; } else if (prevCmd == 'l' || prevCmd == 'L') { cmd = prevCmd; break; } default: { ph.advance(); prevCmd = cmd; } } boolean wasCurve = false; switch (cmd) { case 'M': case 'm': { float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'm') { subPathStartX += x; subPathStartY += y; p.rMoveTo(x, y); lastX += x; lastY += y; } else { subPathStartX = x; subPathStartY = y; p.moveTo(x, y); lastX = x; lastY = y; } break; } case 'Z': case 'z': { p.close(); p.moveTo(subPathStartX, subPathStartY); lastX = subPathStartX; lastY = subPathStartY; lastX1 = subPathStartX; lastY1 = subPathStartY; wasCurve = true; break; } case 'L': case 'l': { float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'l') { p.rLineTo(x, y); lastX += x; lastY += y; } else { p.lineTo(x, y); lastX = x; lastY = y; } break; } case 'H': case 'h': { float x = ph.nextFloat(); if (cmd == 'h') { p.rLineTo(x, 0); lastX += x; } else { p.lineTo(x, lastY); lastX = x; } break; } case 'V': case 'v': { float y = ph.nextFloat(); if (cmd == 'v') { p.rLineTo(0, y); lastY += y; } else { p.lineTo(lastX, y); lastY = y; } break; } case 'C': case 'c': { wasCurve = true; float x1 = ph.nextFloat(); float y1 = ph.nextFloat(); float x2 = ph.nextFloat(); float y2 = ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'c') { x1 += lastX; x2 += lastX; x += lastX; y1 += lastY; y2 += lastY; y += lastY; } p.cubicTo(x1, y1, x2, y2, x, y); lastX1 = x2; lastY1 = y2; lastX = x; lastY = y; break; } case 'S': case 's': { wasCurve = true; float x2 = ph.nextFloat(); float y2 = ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 's') { x2 += lastX; x += lastX; y2 += lastY; y += lastY; } float x1 = 2 * lastX - lastX1; float y1 = 2 * lastY - lastY1; p.cubicTo(x1, y1, x2, y2, x, y); lastX1 = x2; lastY1 = y2; lastX = x; lastY = y; break; } case 'A': case 'a': { float rx = ph.nextFloat(); float ry = ph.nextFloat(); float theta = ph.nextFloat(); int largeArc = (int) ph.nextFloat(); int sweepArc = (int) ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc); lastX = x; lastY = y; break; } } if (!wasCurve) { lastX1 = lastX; lastY1 = lastY; } ph.skipWhitespace(); } return p; }
// draw path 96x96 public static void calcTurnPath(Path pathForTurn, TurnType turnType, Matrix transform) { if (turnType == null) { return; } pathForTurn.reset(); int c = 48; int w = 16; pathForTurn.moveTo(c, 94); float sarrowL = 30; // side of arrow float harrowL = (float) Math.sqrt(2) * sarrowL; // hypotenuse of arrow float spartArrowL = (float) ((sarrowL - w / Math.sqrt(2)) / 2); float hpartArrowL = (float) (harrowL - w) / 2; if (TurnType.C.equals(turnType.getValue())) { int h = 65; pathForTurn.rMoveTo(w / 2, 0); pathForTurn.rLineTo(0, -h); pathForTurn.rLineTo(hpartArrowL, 0); pathForTurn.rLineTo(-harrowL / 2, -harrowL / 2); // center pathForTurn.rLineTo(-harrowL / 2, harrowL / 2); pathForTurn.rLineTo(hpartArrowL, 0); pathForTurn.rLineTo(0, h); } else if (TurnType.TR.equals(turnType.getValue()) || TurnType.TL.equals(turnType.getValue())) { int b = TurnType.TR.equals(turnType.getValue()) ? 1 : -1; int h = 36; float quadShiftX = 22; float quadShiftY = 22; pathForTurn.rMoveTo(-b * 8, 0); pathForTurn.rLineTo(0, -h); pathForTurn.rQuadTo(0, -quadShiftY, b * quadShiftX, -quadShiftY); pathForTurn.rLineTo(0, hpartArrowL); pathForTurn.rLineTo(b * harrowL / 2, -harrowL / 2); // center pathForTurn.rLineTo(-b * harrowL / 2, -harrowL / 2); pathForTurn.rLineTo(0, hpartArrowL); pathForTurn.rQuadTo(-b * (quadShiftX + w), 0, -b * (quadShiftX + w), quadShiftY + w); pathForTurn.rLineTo(0, h); } else if (TurnType.TSLR.equals(turnType.getValue()) || TurnType.TSLL.equals(turnType.getValue())) { int b = TurnType.TSLR.equals(turnType.getValue()) ? 1 : -1; int h = 40; int quadShiftY = 22; float quadShiftX = (float) (quadShiftY / (1 + Math.sqrt(2))); float nQuadShiftX = (sarrowL - 2 * spartArrowL) - quadShiftX - w; float nQuadShifty = quadShiftY + (sarrowL - 2 * spartArrowL); pathForTurn.rMoveTo(-b * 4, 0); pathForTurn.rLineTo(0, -h /* + partArrowL */); pathForTurn.rQuadTo( 0, -quadShiftY + quadShiftX /*- partArrowL*/, b * quadShiftX, -quadShiftY /*- partArrowL*/); pathForTurn.rLineTo(b * spartArrowL, spartArrowL); pathForTurn.rLineTo(0, -sarrowL); // center pathForTurn.rLineTo(-b * sarrowL, 0); pathForTurn.rLineTo(b * spartArrowL, spartArrowL); pathForTurn.rQuadTo(b * nQuadShiftX, -nQuadShiftX, b * nQuadShiftX, nQuadShifty); pathForTurn.rLineTo(0, h); } else if (TurnType.TSHR.equals(turnType.getValue()) || TurnType.TSHL.equals(turnType.getValue())) { int b = TurnType.TSHR.equals(turnType.getValue()) ? 1 : -1; int h = 45; float quadShiftX = 22; float quadShiftY = -(float) (quadShiftX / (1 + Math.sqrt(2))); float nQuadShiftX = -(sarrowL - 2 * spartArrowL) - quadShiftX - w; float nQuadShiftY = -quadShiftY + (sarrowL - 2 * spartArrowL); pathForTurn.rMoveTo(-b * 8, 0); pathForTurn.rLineTo(0, -h); pathForTurn.rQuadTo(0, -(quadShiftX - quadShiftY), b * quadShiftX, quadShiftY); pathForTurn.rLineTo(-b * spartArrowL, spartArrowL); pathForTurn.rLineTo(b * sarrowL, 0); // center pathForTurn.rLineTo(0, -sarrowL); pathForTurn.rLineTo(-b * spartArrowL, spartArrowL); pathForTurn.rCubicTo( b * nQuadShiftX / 2, nQuadShiftX / 2, b * nQuadShiftX, nQuadShiftX / 2, b * nQuadShiftX, nQuadShiftY); pathForTurn.rLineTo(0, h); } else if (TurnType.TU.equals(turnType.getValue())) { int h = 54; float quadShiftX = 13; float quadShiftY = 13; pathForTurn.rMoveTo(28, 0); pathForTurn.rLineTo(0, -h); pathForTurn.rQuadTo(0, -(quadShiftY + w), -(quadShiftX + w), -(quadShiftY + w)); pathForTurn.rQuadTo(-(quadShiftX + w), 0, -(quadShiftX + w), (quadShiftY + w)); pathForTurn.rLineTo(-hpartArrowL, 0); pathForTurn.rLineTo(harrowL / 2, harrowL / 2); // center pathForTurn.rLineTo(harrowL / 2, -harrowL / 2); pathForTurn.rLineTo(-hpartArrowL, 0); pathForTurn.rQuadTo(0, -quadShiftX, quadShiftX, -quadShiftY); pathForTurn.rQuadTo(quadShiftX, 0, quadShiftX, quadShiftY); pathForTurn.rLineTo(0, h); } else if (turnType != null && turnType.isRoundAbout()) { float t = turnType.getTurnAngle(); if (t >= 170 && t < 220) { t = 220; } else if (t > 160 && t < 170) { t = 160; } float sweepAngle = (t - 360) - 180; if (sweepAngle < -360) { sweepAngle += 360; } float r1 = 32f; float r2 = 24f; float angleToRot = 0.3f; pathForTurn.moveTo(48, 48 + r1 + 8); pathForTurn.lineTo(48, 48 + r1); RectF r = new RectF(48 - r1, 48 - r1, 48 + r1, 48 + r1); pathForTurn.arcTo(r, 90, sweepAngle); float angleRad = (float) ((180 + sweepAngle) * Math.PI / 180f); pathForTurn.lineTo( 48 + (r1 + 4) * FloatMath.sin(angleRad), 48 - (r1 + 4) * FloatMath.cos(angleRad)); pathForTurn.lineTo( 48 + (r1 + 6) * FloatMath.sin(angleRad + angleToRot / 2), 48 - (r1 + 6) * FloatMath.cos(angleRad + angleToRot / 2)); pathForTurn.lineTo( 48 + (r1 + 12) * FloatMath.sin(angleRad - angleToRot / 2), 48 - (r1 + 12) * FloatMath.cos(angleRad - angleToRot / 2)); pathForTurn.lineTo( 48 + (r1 + 6) * FloatMath.sin(angleRad - 3 * angleToRot / 2), 48 - (r1 + 6) * FloatMath.cos(angleRad - 3 * angleToRot / 2)); pathForTurn.lineTo( 48 + (r1 + 4) * FloatMath.sin(angleRad - angleToRot), 48 - (r1 + 4) * FloatMath.cos(angleRad - angleToRot)); pathForTurn.lineTo( 48 + r2 * FloatMath.sin(angleRad - angleToRot), 48 - r2 * FloatMath.cos(angleRad - angleToRot)); r.set(48 - r2, 48 - r2, 48 + r2, 48 + r2); pathForTurn.arcTo(r, 360 + sweepAngle + 90, -sweepAngle); pathForTurn.lineTo(40, 48 + r2); pathForTurn.lineTo(40, 48 + r1 + 8); pathForTurn.close(); } pathForTurn.close(); if (transform != null) { pathForTurn.transform(transform); } }