Skip to content

Commit

Permalink
added Matter.Svg.pathToPoints
Browse files Browse the repository at this point in the history
  • Loading branch information
liabru committed Mar 1, 2015
1 parent 2d1ef0d commit 7822ead
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 2 deletions.
233 changes: 233 additions & 0 deletions src/geometry/Svg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/**
* The `Matter.Svg` module contains methods for converting SVG images into sets of `Matter.Vertices`.
*
* See [Demo.js](https:/liabru/matter-js/blob/master/demo/js/Demo.js)
* and [DemoMobile.js](https:/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
*
* @class Svg
*/

var Svg = {};

(function() {

/**
* Converts an SVG path into a `Matter.Vertices` object for the given `Matter.Body`.
* @method pathToPoints
* @param {string} path
* @return {Vector} points
*/
Svg.pathToPoints = function(path) {
var i, il, total, point, distance, segment, segments,
segmentsQueue, lastSegment,
lastPoint, segmentIndex, points = [],
length = 0, x = 0, y = 0;

// prepare helpers functions
var addPoint = function(px, py, pathSegType) {
// all odd-numbered path types are relative, except PATHSEG_CLOSEPATH (1)
var isRelative = pathSegType % 2 === 1 && pathSegType > 1;

// when the last point doesn't equal the current point add the current point
if (!lastPoint || px != lastPoint.x || py != lastPoint.y) {
if (lastPoint && isRelative) {
lx = lastPoint.x;
ly = lastPoint.y;
} else {
lx = 0;
ly = 0;
}

var point = {
x: lx + px,
y: ly + py
};

// set last point
if (isRelative || !lastPoint) {
lastPoint = point;
}

points.push(point);

x = lx + px;
y = ly + py;
}
};

var addSegmentPoint = function(segment) {
var segType = segment.pathSegTypeAsLetter.toUpperCase();

// don't bother processing path ends
if (segType === 'Z')
return;

// map segment to x and y
switch (segType) {

case 'M':
case 'L':
case 'T':
x = segment.x;
y = segment.y;
break;
case 'H':
x = segment.x;
break;
case 'V':
y = segment.y;
break;
case 'C':
x = segment.x;
y = segment.y;
break;
case 'S':
case 'Q':
x = segment.x;
y = segment.y;
break;

}

// add point
addPoint(x, y, segment.pathSegType);
};

// ensure path is absolute
_svgPathToAbsolute(path);

// parse sample value
sample = '1%';

// get total length
total = path.getTotalLength();

// calculate sample distance
if (typeof sample === 'string') {
if (sample.indexOf('%') > -1) {
// sample distance in %
distance = total * (parseFloat(sample) / 100);
} else if (sample.indexOf('px') > -1) {
// fixed sample distance in px
distance = parseFloat(sample);
}
} else {
// specific number of samples
distance = total / sample;
}

// Put all path segments in a queue
segments = [];
for (i = 0; i < path.pathSegList.numberOfItems; i += 1)
segments.push(path.pathSegList.getItem(i));

segmentsQueue = segments.concat();

// sample through path
while (length < total) {
// get segment index
segmentIndex = path.getPathSegAtLength(length);

// get segment
segment = segments[segmentIndex];

// new segment?
if (segment != lastSegment) {
while (segmentsQueue.length && segmentsQueue[0] != segment)
addSegmentPoint(segmentsQueue.shift());

lastSegment = segment;
}

// add points in between when curving
switch (segment.pathSegTypeAsLetter.toUpperCase()) {

case 'C':
case 'T':
case 'S':
case 'Q':
case 'A':
point = path.getPointAtLength(length);
addPoint(point.x, point.y, 0);
break;

}

// increment by sample value
length += distance;
}

// add remaining segments we didn't pass while sampling
for (i = 0, il = segmentsQueue.length; i < il; ++i)
addSegmentPoint(segmentsQueue[i]);

return points;
};

var _svgPathToAbsolute = function(path) {
// http://phrogz.net/convert-svg-path-to-all-absolute-commands

var x0, y0, x1, y1, x2, y2, segs = path.pathSegList,
x = 0, y = 0, len = segs.numberOfItems;

for (var i = 0; i < len; ++i) {
var seg = segs.getItem(i),
c = seg.pathSegTypeAsLetter;

if (/[MLHVCSQTA]/.test(c)) {
if ('x' in seg) x = seg.x;
if ('y' in seg) y = seg.y;
} else {
if ('x1' in seg) x1 = x + seg.x1;
if ('x2' in seg) x2 = x + seg.x2;
if ('y1' in seg) y1 = y + seg.y1;
if ('y2' in seg) y2 = y + seg.y2;
if ('x' in seg) x += seg.x;
if ('y' in seg) y += seg.y;

switch (c) {

case 'm':
segs.replaceItem(path.createSVGPathSegMovetoAbs(x, y), i);
break;
case 'l':
segs.replaceItem(path.createSVGPathSegLinetoAbs(x, y), i);
break;
case 'h':
segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x), i);
break;
case 'v':
segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y), i);
break;
case 'c':
segs.replaceItem(path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i);
break;
case 's':
segs.replaceItem(path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i);
break;
case 'q':
segs.replaceItem(path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i);
break;
case 't':
segs.replaceItem(path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i);
break;
case 'a':
segs.replaceItem(path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i);
break;
case 'z':
case 'Z':
x = x0;
y = y0;
break;

}
}

if (c == 'M' || c == 'm') {
x0 = x;
y0 = y;
}
}
};

})();
2 changes: 0 additions & 2 deletions src/geometry/Vertices.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
* @class Vertices
*/

// TODO: convex decomposition - http://mnbayazit.com/406/bayazit

var Vertices = {};

(function() {
Expand Down
1 change: 1 addition & 0 deletions src/module/Outro.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Matter.RenderPixi = RenderPixi;
Matter.Events = Events;
Matter.Query = Query;
Matter.Runner = Runner;
Matter.Svg = Svg;

// @if DEBUG
Matter.Metrics = Metrics;
Expand Down

0 comments on commit 7822ead

Please sign in to comment.