@@ -961,14 +961,47 @@ else if (lexState == LexState.EXP_HEAD) {
961961 float rx = PApplet .parseFloat (pathTokens [i + 1 ]);
962962 float ry = PApplet .parseFloat (pathTokens [i + 2 ]);
963963 float angle = PApplet .parseFloat (pathTokens [i + 3 ]);
964- boolean fa = PApplet .parseFloat (pathTokens [i + 4 ]) != 0 ;
965- boolean fs = PApplet .parseFloat (pathTokens [i + 5 ]) != 0 ;
966- float endX = PApplet .parseFloat (pathTokens [i + 6 ]);
967- float endY = PApplet .parseFloat (pathTokens [i + 7 ]);
964+ // In compact arc notation, flags and coordinates may be concatenated.
965+ // e.g. "013" is parsed as large-arc=0, sweep=1, x=3
966+ String token4 = pathTokens [i + 4 ];
967+ boolean fa ;
968+ boolean fs ;
969+ float endX ;
970+ float endY ;
971+ int tokenOffset = 0 ;
972+ if (isCompactArcNotation (token4 )) {
973+ fa = token4 .charAt (0 ) == '1' ;
974+ fs = token4 .charAt (1 ) == '1' ;
975+ // Case: flags and x-coordinate are concatenated (e.g. "01100")
976+ // token4 contains flags + x, so y is at i+5.
977+ // We consume 2 fewer tokens than standard (8-2=6).
978+ if (token4 .length () > 2 ) {
979+ endX = PApplet .parseFloat (token4 .substring (2 ));
980+ endY = PApplet .parseFloat (pathTokens [i + 5 ]);
981+ tokenOffset = -2 ;
982+ } else {
983+ // Case: flags are concatenated but separated from x (e.g. "01 100")
984+ // token4 is flags, x is at i+5, y is at i+6.
985+ // We consume 1 fewer token than standard (8-1=7).
986+ endX = PApplet .parseFloat (pathTokens [i + 5 ]);
987+ endY = PApplet .parseFloat (pathTokens [i + 6 ]);
988+ tokenOffset = -1 ;
989+ }
990+ } else {
991+ // Standard notation: flags and coordinates are separate tokens.
992+ // The 'A' command takes 7 arguments:
993+ // rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
994+ // Here, we've already parsed rx (i+1), ry (i+2), and angle (i+3).
995+ // token4 (i+4) is the large-arc-flag.
996+ fa = PApplet .parseFloat (token4 ) != 0 ;
997+ fs = PApplet .parseFloat (pathTokens [i + 5 ]) != 0 ; // sweep-flag
998+ endX = PApplet .parseFloat (pathTokens [i + 6 ]); // x
999+ endY = PApplet .parseFloat (pathTokens [i + 7 ]); // y
1000+ }
9681001 parsePathArcto (cx , cy , rx , ry , angle , fa , fs , endX , endY );
9691002 cx = endX ;
9701003 cy = endY ;
971- i += 8 ;
1004+ i += 8 + tokenOffset ;
9721005 prevCurve = true ;
9731006 }
9741007 break ;
@@ -978,14 +1011,41 @@ else if (lexState == LexState.EXP_HEAD) {
9781011 float rx = PApplet .parseFloat (pathTokens [i + 1 ]);
9791012 float ry = PApplet .parseFloat (pathTokens [i + 2 ]);
9801013 float angle = PApplet .parseFloat (pathTokens [i + 3 ]);
981- boolean fa = PApplet .parseFloat (pathTokens [i + 4 ]) != 0 ;
982- boolean fs = PApplet .parseFloat (pathTokens [i + 5 ]) != 0 ;
983- float endX = cx + PApplet .parseFloat (pathTokens [i + 6 ]);
984- float endY = cy + PApplet .parseFloat (pathTokens [i + 7 ]);
1014+ String token4 = pathTokens [i + 4 ];
1015+ boolean fa ;
1016+ boolean fs ;
1017+ float endX ;
1018+ float endY ;
1019+ int tokenOffset = 0 ;
1020+ if (isCompactArcNotation (token4 )) {
1021+ fa = token4 .charAt (0 ) == '1' ;
1022+ fs = token4 .charAt (1 ) == '1' ;
1023+ // Case: flags and x-coordinate are concatenated
1024+ if (token4 .length () > 2 ) {
1025+ endX = cx + PApplet .parseFloat (token4 .substring (2 ));
1026+ endY = cy + PApplet .parseFloat (pathTokens [i + 5 ]);
1027+ tokenOffset = -2 ;
1028+ } else {
1029+ // Case: flags are concatenated but separated from x
1030+ endX = cx + PApplet .parseFloat (pathTokens [i + 5 ]);
1031+ endY = cy + PApplet .parseFloat (pathTokens [i + 6 ]);
1032+ tokenOffset = -1 ;
1033+ }
1034+ } else {
1035+ // Standard notation: flags and coordinates are separate tokens.
1036+ // The 'a' command takes 7 arguments:
1037+ // rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
1038+ // Here, we've already parsed rx (i+1), ry (i+2), and angle (i+3).
1039+ // token4 (i+4) is the large-arc-flag.
1040+ fa = PApplet .parseFloat (token4 ) != 0 ;
1041+ fs = PApplet .parseFloat (pathTokens [i + 5 ]) != 0 ; // sweep-flag
1042+ endX = cx + PApplet .parseFloat (pathTokens [i + 6 ]); // x
1043+ endY = cy + PApplet .parseFloat (pathTokens [i + 7 ]); // y
1044+ }
9851045 parsePathArcto (cx , cy , rx , ry , angle , fa , fs , endX , endY );
9861046 cx = endX ;
9871047 cy = endY ;
988- i += 8 ;
1048+ i += 8 + tokenOffset ;
9891049 prevCurve = true ;
9901050 }
9911051 break ;
@@ -1054,6 +1114,33 @@ private void parsePathMoveto(float px, float py) {
10541114 }
10551115
10561116
1117+ /**
1118+ * Checks if a token represents compact arc notation where flags and coordinates
1119+ * are concatenated (e.g., "013" for large-arc=0, sweep=1, x=3).
1120+ *
1121+ * @param token the token to check
1122+ * @return true if the token is in compact arc notation format
1123+ */
1124+ private boolean isCompactArcNotation (String token ) {
1125+ if (token == null ) {
1126+ return false ;
1127+ }
1128+ return token .length () > 1 &&
1129+ // First two characters must be '0' or '1' (flags)
1130+ (token .charAt (0 ) == '0' || token .charAt (0 ) == '1' ) &&
1131+ (token .charAt (1 ) == '0' || token .charAt (1 ) == '1' ) &&
1132+ // Either it's just the flags (length 2),
1133+ (token .length () == 2 ||
1134+ // Or the flags are followed by the start of a number coordinate
1135+ // (digit, sign, or decimal point)
1136+ (token .length () > 2 && (
1137+ Character .isDigit (token .charAt (2 )) ||
1138+ token .charAt (2 ) == '+' ||
1139+ token .charAt (2 ) == '-' ||
1140+ token .charAt (2 ) == '.' )));
1141+ }
1142+
1143+
10571144 private void parsePathLineto (float px , float py ) {
10581145 parsePathCode (VERTEX );
10591146 parsePathVertex (px , py );
0 commit comments