Skip to content

Commit

Permalink
Move to commands parsing added
Browse files Browse the repository at this point in the history
  • Loading branch information
nfroidure committed Nov 3, 2013
1 parent d10264e commit d7cdfad
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 51 deletions.
67 changes: 44 additions & 23 deletions src/SVGPathDataParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function SVGPathDataParser() {
this.state = SVGPathDataParser.STATE_WSPS,
SVGPathDataParser.STATE_COMMAS;
this.curNumber = '';
this.curCoords = {};
this.curCommand = null;
this.commands = [];
this.read = function(str) {
if(this.state === SVGPathDataParser.STATE_ENDED) {
Expand Down Expand Up @@ -121,22 +121,38 @@ function SVGPathDataParser() {
}
// New number
if(this.curNumber) {
// Horizontal move to
// Horizontal move to command (x)
if(this.state&SVGPathDataParser.STATE_HORIZ) {
this.curCommand = null;
this.commands.push({
type: SVGPathDataParser.STATE_HORIZ,
type: this.state&SVGPathDataParser.STATE_COMMANDS_MASK,
relative: !!(this.state&SVGPathDataParser.STATE_RELATIVE),
x: this.curNumber
});
this.state |= SVGPathDataParser.STATE_NUMBER;
// Vertical move to
// Vertical move to command (y)
} else if(this.state&SVGPathDataParser.STATE_VERT) {
this.curCommand = null;
this.commands.push({
type: SVGPathDataParser.STATE_VERT,
type: this.state&SVGPathDataParser.STATE_COMMANDS_MASK,
relative: !!(this.state&SVGPathDataParser.STATE_RELATIVE),
x: this.curNumber
y: this.curNumber
});
this.state |= SVGPathDataParser.STATE_NUMBER;
// Move to / line to commands (x, y)
} else if(this.state&SVGPathDataParser.STATE_MOVETO) {
if(null === this.curCommand) {
this.curCommand = {
type: this.state&SVGPathDataParser.STATE_COMMANDS_MASK,
relative: !!(this.state&SVGPathDataParser.STATE_RELATIVE),
x: this.curNumber
};
} else {
this.curCommand.y = this.curNumber;
this.commands.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
}
this.curNumber = '';
// Continue if a white space or a comma was detected
Expand All @@ -146,6 +162,11 @@ function SVGPathDataParser() {
}
// End of a command
if(-1 !== COMMANDS.indexOf(str[i]) || -1 !== EOT.indexOf(str[i])) {
// Adding residual command
if(null !== this.curCommand) {
this.commands.push(this.curCommand);
this.curCommand = null;
}
// Ending the stream
if(-1 !== EOT.indexOf(str[i])) {
this.state = SVGPathDataParser.STATE_ENDED;
Expand All @@ -157,28 +178,28 @@ function SVGPathDataParser() {
}
// Detecting the next command
this.state ^= this.state&SVGPathDataParser.STATE_COMMANDS_MASK;
// Vertical move to
// Horizontal move to command
if('h' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_HORIZ |
SVGPathDataParser.STATE_COMMAS_WSPS | SVGPathDataParser.STATE_NUMBER;
if(str[i]==='h') {
this.state |= SVGPathDataParser.STATE_RELATIVE;
} else {
this.state ^= this.state&SVGPathDataParser.STATE_RELATIVE;
}
// Vertical move to
this.state |= SVGPathDataParser.STATE_HORIZ;
// Vertical move to command
} else if('v' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_VERT |
SVGPathDataParser.STATE_COMMAS_WSPS | SVGPathDataParser.STATE_NUMBER;
if(str[i]==='v') {
this.state |= SVGPathDataParser.STATE_RELATIVE;
} else {
this.state ^= this.state&SVGPathDataParser.STATE_RELATIVE;
}
this.state |= SVGPathDataParser.STATE_VERT;
// Move to command
} else if('m' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_MOVETO;
// Unkown command
} else {
throw Error('Unexpected character "' + str[i] + '" at index ' + i + '.');
}
// Is the command relative
if(str[i]===str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_RELATIVE;
} else {
this.state ^= this.state&SVGPathDataParser.STATE_RELATIVE;
}
// White spaces ca follows a command
this.state |= SVGPathDataParser.STATE_COMMAS_WSPS |
SVGPathDataParser.STATE_NUMBER;
}
return this;
};
Expand Down Expand Up @@ -221,7 +242,7 @@ SVGPathDataParser.STATE_SMOOTHTO = 131072; // Smooth curve to command (s/S)
SVGPathDataParser.STATE_QUADTO = 262144; // Quadratic bezier curve to command (q/Q)
SVGPathDataParser.STATE_SMOOTHQUADTO = 524288; // Smooth quadratic bezier curve to command (t/T)
SVGPathDataParser.STATE_ARC = 1048576; // Elliptic arc (a/A)
SVGPathDataParser.STATE_COMMANDS_MASK = SVGPathDataParser.STATE_RELATIVE |
SVGPathDataParser.STATE_COMMANDS_MASK =
SVGPathDataParser.STATE_CLOSEPATH | SVGPathDataParser.STATE_MOVETO |
SVGPathDataParser.STATE_LINETO | SVGPathDataParser.STATE_HORIZ |
SVGPathDataParser.STATE_VERT | SVGPathDataParser.STATE_CURVETO |
Expand Down
56 changes: 28 additions & 28 deletions test/hv.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,91 +118,91 @@ describe("Parsing vertical commands", function() {
var commands = parser.parse('V100');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with single complexer coordinate", function() {
var commands = parser.parse('V-10e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10e-5');
assert.equal(commands[0].y, '-10e-5');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with single even more complexer coordinate", function() {
var commands = parser.parse('V-10.0032e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
assert.equal(commands[0].y, '-10.0032e-5');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with single relative coordinate", function() {
var commands = parser.parse('v100');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with comma separated coordinates", function() {
var commands = parser.parse('V123,456,7890,9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[1].x, '456');
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[2].x, '7890');
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[3].x, '9876');
assert.equal(commands[3].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with space separated coordinates", function() {
var commands = parser.parse('V123 456 7890 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[1].x, '456');
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[2].x, '7890');
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[3].x, '9876');
assert.equal(commands[3].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with nested separated coordinates", function() {
var commands = parser.parse('V123 , 456 \t,\n7890 \r\n 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[1].x, '456');
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[2].x, '7890');
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[3].x, '9876');
assert.equal(commands[3].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with multiple command declarations", function() {
var commands = parser.parse('V123 , 456 \t,\n7890 \r\n 9876V123 , 456 \t,\n7890 \r\n 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[1].x, '456');
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[2].x, '7890');
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[3].x, '9876');
assert.equal(commands[3].y, '9876');
assert.equal(commands[4].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[4].x, '123');
assert.equal(commands[4].y, '123');
assert.equal(commands[5].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[5].x, '456');
assert.equal(commands[5].y, '456');
assert.equal(commands[6].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[6].x, '7890');
assert.equal(commands[6].y, '7890');
assert.equal(commands[7].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[7].x, '9876');
assert.equal(commands[7].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

Expand All @@ -222,25 +222,25 @@ describe("Parsing nested vertical/horizontal commands", function() {
var commands = parser.parse('V100H100v0.12h0.12,V100,h100v-10e-5 H-10e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
assert.equal(commands[1].type, SVGPathDataParser.STATE_HORIZ);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '100');
assert.equal(commands[2].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, '0.12');
assert.equal(commands[2].y, '0.12');
assert.equal(commands[3].type, SVGPathDataParser.STATE_HORIZ);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '0.12');
assert.equal(commands[4].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[4].relative, false);
assert.equal(commands[4].x, '100');
assert.equal(commands[4].y, '100');
assert.equal(commands[5].type, SVGPathDataParser.STATE_HORIZ);
assert.equal(commands[5].relative, true);
assert.equal(commands[5].x, '100');
assert.equal(commands[6].type, SVGPathDataParser.STATE_VERT);
assert.equal(commands[6].relative, true);
assert.equal(commands[6].x, '-10e-5');
assert.equal(commands[6].y, '-10e-5');
assert.equal(commands[7].type, SVGPathDataParser.STATE_HORIZ);
assert.equal(commands[7].relative, false);
assert.equal(commands[7].x, '-10e-5');
Expand Down
125 changes: 125 additions & 0 deletions test/moveto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
var assert = chai.assert;

describe("Parsing move to commands", function() {
var parser;

beforeEach(function() {
parser = new SVGPathDataParser();
});

afterEach(function() {
});

it("should work with single coordinate", function() {
var commands = parser.parse('M100');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with comma separated coordinates", function() {
var commands = parser.parse('M100,100');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with space separated coordinates", function() {
var commands = parser.parse('m100 \t 100');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with single complexer coordinate", function() {
var commands = parser.parse('m-10e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '-10e-5');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with complexer coordinates", function() {
var commands = parser.parse('m-10e-5 -10e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '-10e-5');
assert.equal(commands[0].y, '-10e-5');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with single even more complexer coordinates", function() {
var commands = parser.parse('M-10.0032e-5 -10.0032e-5');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
assert.equal(commands[0].y, '-10.0032e-5');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with comma separated coordinate pairs", function() {
var commands = parser.parse('M123,456 7890,9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with space separated coordinate pairs", function() {
var commands = parser.parse('m123 \t 456 \n 7890 \r 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with nested separated coordinates", function() {
var commands = parser.parse('M123 , 456 \t,\n7890 \r\n 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

it("should work with multiple command declarations", function() {
var commands = parser.parse('M123 , 456 \t,\n7890 \r\n 9876m123 , 456 \t,\n7890 \r\n 9876');
assert.equal(commands[0].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(commands[2].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, '123');
assert.equal(commands[2].y, '456');
assert.equal(commands[3].type, SVGPathDataParser.STATE_MOVETO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '7890');
assert.equal(commands[3].y, '9876');
assert.equal(parser.state, SVGPathDataParser.STATE_ENDED);
});

});

0 comments on commit d7cdfad

Please sign in to comment.