Jelajahi Sumber

SVGLoader: New parseFloats() implementation.

Mugen87 4 tahun lalu
induk
melakukan
a81e7b7916
2 mengubah file dengan 424 tambahan dan 24 penghapusan
  1. 212 12
      examples/js/loaders/SVGLoader.js
  2. 212 12
      examples/jsm/loaders/SVGLoader.js

+ 212 - 12
examples/js/loaders/SVGLoader.js

@@ -973,35 +973,235 @@ THREE.SVGLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 		}
 
-		function parseFloats( string ) {
+		// from https://github.com/ppvg/svg-numbers (MIT License)
 
-			var array = string.split( /[\s,]+|(?=\s?[+\-])/ );
+		function parseFloats( input ) {
 
-			for ( var i = 0; i < array.length; i ++ ) {
+			if ( typeof input !== 'string' ) {
 
-				var number = array[ i ];
+				throw new TypeError( 'Invalid input: ' + typeof input );
 
-				// Handle values like 48.6037.7.8
-				// TODO Find a regex for this
+			}
+
+			// Character groups
+			var RE = {
+				SEPARATOR: /[ \t\r\n\,.\-+]/,
+				WHITESPACE: /[ \t\r\n]/,
+				DIGIT: /[\d]/,
+				SIGN: /[-+]/,
+				POINT: /\./,
+				COMMA: /,/,
+				EXP: /e/i
+			};
+
+			// States
+			var SEP = 0;
+			var INT = 1;
+			var FLOAT = 2;
+			var EXP = 3;
+
+			var state = SEP;
+			var seenComma = true;
+			var result = [], number = '', exponent = '';
+
+			function throwSyntaxError( current, i, partial ) {
+
+				var error = new SyntaxError( 'Unexpected character "' + current + '" at index ' + i + '.' );
+				error.partial = partial;
+				throw error;
+
+			}
+
+			function newNumber() {
+
+				if ( number !== '' ) {
+
+					if ( exponent === '' ) result.push( Number( number ) );
+					else result.push( Number( number ) * Math.pow( 10, Number( exponent ) ) );
+
+				}
+
+				number = '';
+				exponent = '';
+
+			}
+
+			var current, i = 0, length = input.length;
+			for ( i = 0; i < length; i ++ ) {
+
+				current = input[ i ];
+
+				// parse until next number
+				if ( state === SEP ) {
+
+					// eat whitespace
+					if ( RE.WHITESPACE.test( current ) ) {
+
+						continue;
+
+					}
+
+					// start new number
+					if ( RE.DIGIT.test( current ) || RE.SIGN.test( current ) ) {
+
+						state = INT;
+						number = current;
+						continue;
+
+					}
+
+					if ( RE.POINT.test( current ) ) {
+
+						state = FLOAT;
+						number = current;
+						continue;
+
+					}
+
+					// throw on double commas (e.g. "1, , 2")
+					if ( RE.COMMA.test( current ) ) {
+
+						if ( seenComma ) {
+
+							throwSyntaxError( current, i, result );
+
+						}
+
+						seenComma = true;
+
+					}
+
+				}
+
+				// parse integer part
+				if ( state === INT ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						number += current;
+						continue;
+
+					}
+
+					if ( RE.POINT.test( current ) ) {
+
+						number += current;
+						state = FLOAT;
+						continue;
+
+					}
+
+					if ( RE.EXP.test( current ) ) {
 
-				if ( number.indexOf( '.' ) !== number.lastIndexOf( '.' ) ) {
+						state = EXP;
+						continue;
 
-					var split = number.split( '.' );
+					}
+
+					// throw on double signs ("-+1"), but not on sign as separator ("-1-2")
+					if ( RE.SIGN.test( current )
+							&& number.length === 1
+							&& RE.SIGN.test( number[ 0 ] ) ) {
+
+						throwSyntaxError( current, i, result );
+
+					}
+
+				}
+
+				// parse decimal part
+				if ( state === FLOAT ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						number += current;
+						continue;
+
+					}
 
-					for ( var s = 2; s < split.length; s ++ ) {
+					if ( RE.EXP.test( current ) ) {
 
-						array.splice( i + s - 1, 0, '0.' + split[ s ] );
+						state = EXP;
+						continue;
+
+					}
+
+					// throw on double decimal points (e.g. "1..2")
+					if ( RE.POINT.test( current ) && number[ number.length - 1 ] === '.' ) {
+
+						throwSyntaxError( current, i, result );
 
 					}
 
 				}
 
-				array[ i ] = parseFloatWithUnits( number );
+				// parse exponent part
+				if ( state == EXP ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						exponent += current;
+						continue;
+
+					}
+
+					if ( RE.SIGN.test( current ) ) {
+
+						if ( exponent === '' ) {
+
+							exponent += current;
+							continue;
+
+						}
+
+						if ( exponent.length === 1 && RE.SIGN.test( exponent ) ) {
+
+							throwSyntaxError( current, i, result );
+
+						}
+
+					}
+
+				}
+
+
+				// end of number
+				if ( RE.WHITESPACE.test( current ) ) {
+
+					newNumber();
+					state = SEP;
+					seenComma = false;
+
+				} else if ( RE.COMMA.test( current ) ) {
+
+					newNumber();
+					state = SEP;
+					seenComma = true;
+
+				} else if ( RE.SIGN.test( current ) ) {
+
+					newNumber();
+					state = INT;
+					number = current;
+
+				} else if ( RE.POINT.test( current ) ) {
+
+					newNumber();
+					state = FLOAT;
+					number = current;
+
+				} else {
+
+					throwSyntaxError( current, i, result );
+
+				}
 
 			}
 
-			return array;
+			// add the last number found (if any)
+			newNumber();
 
+			return result;
 
 		}
 

+ 212 - 12
examples/jsm/loaders/SVGLoader.js

@@ -985,35 +985,235 @@ SVGLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 		}
 
-		function parseFloats( string ) {
+		// from https://github.com/ppvg/svg-numbers (MIT License)
 
-			var array = string.split( /[\s,]+|(?=\s?[+\-])/ );
+		function parseFloats( input ) {
 
-			for ( var i = 0; i < array.length; i ++ ) {
+			if ( typeof input !== 'string' ) {
 
-				var number = array[ i ];
+				throw new TypeError( 'Invalid input: ' + typeof input );
 
-				// Handle values like 48.6037.7.8
-				// TODO Find a regex for this
+			}
+
+			// Character groups
+			var RE = {
+				SEPARATOR: /[ \t\r\n\,.\-+]/,
+				WHITESPACE: /[ \t\r\n]/,
+				DIGIT: /[\d]/,
+				SIGN: /[-+]/,
+				POINT: /\./,
+				COMMA: /,/,
+				EXP: /e/i
+			};
+
+			// States
+			var SEP = 0;
+			var INT = 1;
+			var FLOAT = 2;
+			var EXP = 3;
+
+			var state = SEP;
+			var seenComma = true;
+			var result = [], number = '', exponent = '';
+
+			function throwSyntaxError( current, i, partial ) {
+
+				var error = new SyntaxError( 'Unexpected character "' + current + '" at index ' + i + '.' );
+				error.partial = partial;
+				throw error;
+
+			}
+
+			function newNumber() {
+
+				if ( number !== '' ) {
+
+					if ( exponent === '' ) result.push( Number( number ) );
+					else result.push( Number( number ) * Math.pow( 10, Number( exponent ) ) );
+
+				}
+
+				number = '';
+				exponent = '';
+
+			}
+
+			var current, i = 0, length = input.length;
+			for ( i = 0; i < length; i ++ ) {
+
+				current = input[ i ];
+
+				// parse until next number
+				if ( state === SEP ) {
+
+					// eat whitespace
+					if ( RE.WHITESPACE.test( current ) ) {
+
+						continue;
+
+					}
+
+					// start new number
+					if ( RE.DIGIT.test( current ) || RE.SIGN.test( current ) ) {
+
+						state = INT;
+						number = current;
+						continue;
+
+					}
+
+					if ( RE.POINT.test( current ) ) {
+
+						state = FLOAT;
+						number = current;
+						continue;
+
+					}
+
+					// throw on double commas (e.g. "1, , 2")
+					if ( RE.COMMA.test( current ) ) {
+
+						if ( seenComma ) {
+
+							throwSyntaxError( current, i, result );
+
+						}
+
+						seenComma = true;
+
+					}
+
+				}
+
+				// parse integer part
+				if ( state === INT ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						number += current;
+						continue;
+
+					}
+
+					if ( RE.POINT.test( current ) ) {
+
+						number += current;
+						state = FLOAT;
+						continue;
+
+					}
+
+					if ( RE.EXP.test( current ) ) {
 
-				if ( number.indexOf( '.' ) !== number.lastIndexOf( '.' ) ) {
+						state = EXP;
+						continue;
 
-					var split = number.split( '.' );
+					}
+
+					// throw on double signs ("-+1"), but not on sign as separator ("-1-2")
+					if ( RE.SIGN.test( current )
+							&& number.length === 1
+							&& RE.SIGN.test( number[ 0 ] ) ) {
+
+						throwSyntaxError( current, i, result );
+
+					}
+
+				}
+
+				// parse decimal part
+				if ( state === FLOAT ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						number += current;
+						continue;
+
+					}
 
-					for ( var s = 2; s < split.length; s ++ ) {
+					if ( RE.EXP.test( current ) ) {
 
-						array.splice( i + s - 1, 0, '0.' + split[ s ] );
+						state = EXP;
+						continue;
+
+					}
+
+					// throw on double decimal points (e.g. "1..2")
+					if ( RE.POINT.test( current ) && number[ number.length - 1 ] === '.' ) {
+
+						throwSyntaxError( current, i, result );
 
 					}
 
 				}
 
-				array[ i ] = parseFloatWithUnits( number );
+				// parse exponent part
+				if ( state == EXP ) {
+
+					if ( RE.DIGIT.test( current ) ) {
+
+						exponent += current;
+						continue;
+
+					}
+
+					if ( RE.SIGN.test( current ) ) {
+
+						if ( exponent === '' ) {
+
+							exponent += current;
+							continue;
+
+						}
+
+						if ( exponent.length === 1 && RE.SIGN.test( exponent ) ) {
+
+							throwSyntaxError( current, i, result );
+
+						}
+
+					}
+
+				}
+
+
+				// end of number
+				if ( RE.WHITESPACE.test( current ) ) {
+
+					newNumber();
+					state = SEP;
+					seenComma = false;
+
+				} else if ( RE.COMMA.test( current ) ) {
+
+					newNumber();
+					state = SEP;
+					seenComma = true;
+
+				} else if ( RE.SIGN.test( current ) ) {
+
+					newNumber();
+					state = INT;
+					number = current;
+
+				} else if ( RE.POINT.test( current ) ) {
+
+					newNumber();
+					state = FLOAT;
+					number = current;
+
+				} else {
+
+					throwSyntaxError( current, i, result );
+
+				}
 
 			}
 
-			return array;
+			// add the last number found (if any)
+			newNumber();
 
+			return result;
 
 		}