diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java index e85389f02e..59f54f87f3 100644 --- a/core/src/processing/core/PShapeSVG.java +++ b/core/src/processing/core/PShapeSVG.java @@ -514,53 +514,89 @@ protected void parsePath() { char[] pathDataChars = pathData.toCharArray(); StringBuilder pathBuffer = new StringBuilder(); - boolean lastSeparate = false; - boolean isOnDecimal = false; + + // The states of the lexical sanner + enum LexState { + AFTER_CMD,// Just after a command (i.e. a single alphabet) + NEUTRAL, // Neutral state, waiting for a number expression or a command + INTEGER, // On a sequence of digits possibly led by the '-' sign + DECIMAL, // On a digit sequence following the decimal point '.' + EXP_HEAD, // On the head of the exponent part of a scientific notation; the '-' sign or a digit + EXP_TAIL, // On the integer expression in the exponent part + } + LexState lexState = LexState.NEUTRAL; for (int i = 0; i < pathDataChars.length; i++) { char c = pathDataChars[i]; - boolean separate = false; - - if (c == 'M' || c == 'm' || - c == 'L' || c == 'l' || - c == 'H' || c == 'h' || - c == 'V' || c == 'v' || - c == 'C' || c == 'c' || // beziers - c == 'S' || c == 's' || - c == 'Q' || c == 'q' || // quadratic beziers - c == 'T' || c == 't' || - c == 'A' || c == 'a' || // elliptical arc - c == 'Z' || c == 'z' || // closepath - c == ',') { - separate = true; - if (i != 0) { + + // Put a separator after a command. + if (lexState == LexState.AFTER_CMD) { + pathBuffer.append("|"); + lexState = LexState.NEUTRAL; + } + + if (c >= '0' && c <= '9') { + // If it is a head of a number representation, enter the 'inside' of the digit sequence. + if (lexState == LexState.NEUTRAL) { + lexState = LexState.INTEGER; + } + else if (lexState == LexState.EXP_HEAD) { + lexState = LexState.EXP_TAIL; + } + pathBuffer.append(c); + continue; + } + + if (c == '-') { + if (lexState == LexState.NEUTRAL) { + // In neutral state, enter 'digit sequence'. + lexState = LexState.INTEGER; + } + else if (lexState == LexState.EXP_HEAD) { + // In the begining of an exponent, enter 'exponent digit sequence'. + lexState = LexState.EXP_TAIL; + } + else { + // Otherwise, begin a new number representation. pathBuffer.append("|"); + lexState = LexState.INTEGER; } + pathBuffer.append("-"); + continue; } - if (c == 'Z' || c == 'z') { - separate = false; + + if (c == '.') { + if (lexState == LexState.DECIMAL || lexState == LexState.EXP_HEAD || lexState == LexState.EXP_TAIL) { + // Begin a new decimal number unless it is in a neutral state or after a digit sequence + pathBuffer.append("|"); + } + pathBuffer.append("."); + lexState = LexState.DECIMAL; + continue; } - if (c == '.' && !isOnDecimal) { - isOnDecimal = true; + + if (c == 'e' || c == 'E') { + // Found 'e' or 'E', enter the 'exponent' state immediately. + pathBuffer.append("e"); + lexState = LexState.EXP_HEAD; + continue; } - else if (isOnDecimal && (c < '0' || c > '9')) { + + // The following are executed for non-numeral elements + + if (lexState != LexState.NEUTRAL) { pathBuffer.append("|"); - isOnDecimal = c == '.'; - } - if (c == '-' && !lastSeparate) { - // allow for 'e' notation in numbers, e.g. 2.10e-9 - // https://download.processing.org/bugzilla/1408.html - if (i == 0 || pathDataChars[i-1] != 'e') { - pathBuffer.append("|"); - } + lexState = LexState.NEUTRAL; } + if (c != ',') { - pathBuffer.append(c); //"" + pathDataBuffer.charAt(i)); + pathBuffer.append(c); } - if (separate && c != ',' && c != '-') { - pathBuffer.append("|"); + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + // Every alphabet character except for 'e' and 'E' are considered as a command. + lexState = LexState.AFTER_CMD; } - lastSeparate = separate; } // use whitespace constant to get rid of extra spaces and CR or LF diff --git a/core/test/processing/core/PShapeSVGTest.java b/core/test/processing/core/PShapeSVGTest.java index 2b9ddd96cb..a38c3ae1bc 100644 --- a/core/test/processing/core/PShapeSVGTest.java +++ b/core/test/processing/core/PShapeSVGTest.java @@ -10,23 +10,47 @@ public class PShapeSVGTest { - private static final String TEST_CONTENT = ""; + private static final String[] TEST_CONTENT = { + "", + "" + }; + private static final int[] TEST_NVERTEX = {2, 8}; + private static final String TEST_EXPONENT = + ""; @Test public void testDecimals() { try { - XML xml = XML.parse(TEST_CONTENT); + for (int i = 0; i < TEST_CONTENT.length; ++i) { + XML xml = XML.parse(TEST_CONTENT[i]); + PShapeSVG shape = new PShapeSVG(xml); + PShape[] children = shape.getChildren(); + Assert.assertEquals(1, children.length); + PShape[] grandchildren = children[0].getChildren(); + Assert.assertEquals(1, grandchildren.length); + Assert.assertEquals(0, grandchildren[0].getChildCount()); + Assert.assertEquals(TEST_NVERTEX[i], grandchildren[0].getVertexCount()); + } + } + catch (Exception e) { + Assert.fail("Encountered exception " + e); + } + } + + @Test + public void testExponent() { + try { + XML xml = XML.parse(TEST_EXPONENT); PShapeSVG shape = new PShapeSVG(xml); PShape[] children = shape.getChildren(); Assert.assertEquals(1, children.length); PShape[] grandchildren = children[0].getChildren(); Assert.assertEquals(1, grandchildren.length); Assert.assertEquals(0, grandchildren[0].getChildCount()); - Assert.assertEquals(2, grandchildren[0].getVertexCount()); + Assert.assertEquals(8, grandchildren[0].getVertexCount()); } catch (Exception e) { Assert.fail("Encountered exception " + e); } } - }