From 7822ead3bf1aaab055c1370a1cc38267cc6d621f Mon Sep 17 00:00:00 2001 From: liabru Date: Sun, 1 Mar 2015 15:52:34 +0000 Subject: [PATCH] added Matter.Svg.pathToPoints --- src/geometry/Svg.js | 233 +++++++++++++++++++++++++++++++++++++++ src/geometry/Vertices.js | 2 - src/module/Outro.js | 1 + 3 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 src/geometry/Svg.js diff --git a/src/geometry/Svg.js b/src/geometry/Svg.js new file mode 100644 index 0000000..434ec49 --- /dev/null +++ b/src/geometry/Svg.js @@ -0,0 +1,233 @@ +/** +* The `Matter.Svg` module contains methods for converting SVG images into sets of `Matter.Vertices`. +* +* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) +* and [DemoMobile.js](https://github.com/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; + } + } + }; + +})(); \ No newline at end of file diff --git a/src/geometry/Vertices.js b/src/geometry/Vertices.js index 331f7ed..21e0b1c 100644 --- a/src/geometry/Vertices.js +++ b/src/geometry/Vertices.js @@ -9,8 +9,6 @@ * @class Vertices */ -// TODO: convex decomposition - http://mnbayazit.com/406/bayazit - var Vertices = {}; (function() { diff --git a/src/module/Outro.js b/src/module/Outro.js index dfacc4e..6d9360d 100644 --- a/src/module/Outro.js +++ b/src/module/Outro.js @@ -38,6 +38,7 @@ Matter.RenderPixi = RenderPixi; Matter.Events = Events; Matter.Query = Query; Matter.Runner = Runner; +Matter.Svg = Svg; // @if DEBUG Matter.Metrics = Metrics;