diff --git a/demo/dev.html b/demo/dev.html
index c2c43a6..3edd648 100644
--- a/demo/dev.html
+++ b/demo/dev.html
@@ -6,6 +6,9 @@
+
+
+
@@ -32,6 +35,7 @@
+
diff --git a/demo/js/Demo.js b/demo/js/Demo.js
index 4469942..3d2e7d6 100644
--- a/demo/js/Demo.js
+++ b/demo/js/Demo.js
@@ -130,8 +130,6 @@
});
World.add(_world, compound);
-
- _world.gravity.y = 1;
var renderOptions = _engine.render.options;
renderOptions.showCollisions = true;
@@ -141,6 +139,26 @@
renderOptions.showConvexHulls = true;
};
+ Demo.concave = function() {
+ var _world = _engine.world;
+
+ Demo.reset();
+
+ var vertices = Matter.Vertices.fromPath('354 89,336 118,310 145,302 227,322 271,375 292,490 289,539 271,540 233,549 133,526 100,552 36,601 63,633 122,628 227,594 304,505 340,426 340,327 330,265 294,246 242,246 181,256 133,283 81,346 44');
+ //var vertices = Matter.Vertices.fromPath('164 171,232 233,213 302,273 241,342 305,316 231,364 170,309 188,281 117,240 182');
+
+ var concave = Bodies.fromVertices(200, 200, vertices);
+ World.add(_world, concave);
+
+ var renderOptions = _engine.render.options;
+ renderOptions.showCollisions = true;
+ renderOptions.showBounds = true;
+ renderOptions.showAxes = true;
+ renderOptions.showPositions = true;
+ renderOptions.showConvexHulls = true;
+ //renderOptions.showVertexNumbers = true;
+ };
+
Demo.slingshot = function() {
var _world = _engine.world;
diff --git a/demo/js/lib/decomp.js b/demo/js/lib/decomp.js
new file mode 100644
index 0000000..f16bc45
--- /dev/null
+++ b/demo/js/lib/decomp.js
@@ -0,0 +1,670 @@
+!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.decomp=e():"undefined"!=typeof global?global.decomp=e():"undefined"!=typeof self&&(self.decomp=e())}(function(){var define,module,exports;
+return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=0 && s<=1 && t>=0 && t<=1);
+};
+
+
+},{"./Scalar":4}],2:[function(require,module,exports){
+module.exports = Point;
+
+/**
+ * Point related functions
+ * @class Point
+ */
+function Point(){};
+
+/**
+ * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order.
+ * @static
+ * @method area
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @return {Number}
+ */
+Point.area = function(a,b,c){
+ return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])));
+};
+
+Point.left = function(a,b,c){
+ return Point.area(a,b,c) > 0;
+};
+
+Point.leftOn = function(a,b,c) {
+ return Point.area(a, b, c) >= 0;
+};
+
+Point.right = function(a,b,c) {
+ return Point.area(a, b, c) < 0;
+};
+
+Point.rightOn = function(a,b,c) {
+ return Point.area(a, b, c) <= 0;
+};
+
+var tmpPoint1 = [],
+ tmpPoint2 = [];
+
+/**
+ * Check if three points are collinear
+ * @method collinear
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision.
+ * @return {Boolean}
+ */
+Point.collinear = function(a,b,c,thresholdAngle) {
+ if(!thresholdAngle)
+ return Point.area(a, b, c) == 0;
+ else {
+ var ab = tmpPoint1,
+ bc = tmpPoint2;
+
+ ab[0] = b[0]-a[0];
+ ab[1] = b[1]-a[1];
+ bc[0] = c[0]-b[0];
+ bc[1] = c[1]-b[1];
+
+ var dot = ab[0]*bc[0] + ab[1]*bc[1],
+ magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]),
+ magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]),
+ angle = Math.acos(dot/(magA*magB));
+ return angle < thresholdAngle;
+ }
+};
+
+Point.sqdist = function(a,b){
+ var dx = b[0] - a[0];
+ var dy = b[1] - a[1];
+ return dx * dx + dy * dy;
+};
+
+},{}],3:[function(require,module,exports){
+var Line = require("./Line")
+, Point = require("./Point")
+, Scalar = require("./Scalar")
+
+module.exports = Polygon;
+
+/**
+ * Polygon class.
+ * @class Polygon
+ * @constructor
+ */
+function Polygon(){
+
+ /**
+ * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..]
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = [];
+}
+
+/**
+ * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle.
+ * @method at
+ * @param {Number} i
+ * @return {Array}
+ */
+Polygon.prototype.at = function(i){
+ var v = this.vertices,
+ s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+};
+
+/**
+ * Get first vertex
+ * @method first
+ * @return {Array}
+ */
+Polygon.prototype.first = function(){
+ return this.vertices[0];
+};
+
+/**
+ * Get last vertex
+ * @method last
+ * @return {Array}
+ */
+Polygon.prototype.last = function(){
+ return this.vertices[this.vertices.length-1];
+};
+
+/**
+ * Clear the polygon data
+ * @method clear
+ * @return {Array}
+ */
+Polygon.prototype.clear = function(){
+ this.vertices.length = 0;
+};
+
+/**
+ * Append points "from" to "to"-1 from an other polygon "poly" onto this one.
+ * @method append
+ * @param {Polygon} poly The polygon to get points from.
+ * @param {Number} from The vertex index in "poly".
+ * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending.
+ * @return {Array}
+ */
+Polygon.prototype.append = function(poly,from,to){
+ if(typeof(from) == "undefined") throw new Error("From is not given!");
+ if(typeof(to) == "undefined") throw new Error("To is not given!");
+
+ if(to-1 < from) throw new Error("lol1");
+ if(to > poly.vertices.length) throw new Error("lol2");
+ if(from < 0) throw new Error("lol3");
+
+ for(var i=from; i v[br][0])) {
+ br = i;
+ }
+ }
+
+ // reverse poly if clockwise
+ if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) {
+ this.reverse();
+ }
+};
+
+/**
+ * Reverse the vertices in the polygon
+ * @method reverse
+ */
+Polygon.prototype.reverse = function(){
+ var tmp = [];
+ for(var i=0, N=this.vertices.length; i!==N; i++){
+ tmp.push(this.vertices.pop());
+ }
+ this.vertices = tmp;
+};
+
+/**
+ * Check if a point in the polygon is a reflex point
+ * @method isReflex
+ * @param {Number} i
+ * @return {Boolean}
+ */
+Polygon.prototype.isReflex = function(i){
+ return Point.right(this.at(i - 1), this.at(i), this.at(i + 1));
+};
+
+var tmpLine1=[],
+ tmpLine2=[];
+
+/**
+ * Check if two vertices in the polygon can see each other
+ * @method canSee
+ * @param {Number} a Vertex index 1
+ * @param {Number} b Vertex index 2
+ * @return {Boolean}
+ */
+Polygon.prototype.canSee = function(a,b) {
+ var p, dist, l1=tmpLine1, l2=tmpLine2;
+
+ if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) {
+ return false;
+ }
+ dist = Point.sqdist(this.at(a), this.at(b));
+ for (var i = 0; i !== this.vertices.length; ++i) { // for each edge
+ if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges
+ continue;
+ if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge
+ l1[0] = this.at(a);
+ l1[1] = this.at(b);
+ l2[0] = this.at(i);
+ l2[1] = this.at(i + 1);
+ p = Line.lineInt(l1,l2);
+ if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b
+ return false;
+ }
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Copy the polygon from vertex i to vertex j.
+ * @method copy
+ * @param {Number} i
+ * @param {Number} j
+ * @param {Polygon} [targetPoly] Optional target polygon to save in.
+ * @return {Polygon} The resulting copy.
+ */
+Polygon.prototype.copy = function(i,j,targetPoly){
+ var p = targetPoly || new Polygon();
+ p.clear();
+ if (i < j) {
+ // Insert all vertices from i to j
+ for(var k=i; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ } else {
+
+ // Insert vertices 0 to j
+ for(var k=0; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ // Insert vertices i to end
+ for(var k=i; k 0)
+ return this.slice(edges);
+ else
+ return [this];
+};
+
+/**
+ * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons.
+ * @method slice
+ * @param {Array} cutEdges A list of edges, as returned by .getCutEdges()
+ * @return {Array}
+ */
+Polygon.prototype.slice = function(cutEdges){
+ if(cutEdges.length == 0) return [this];
+ if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){
+
+ var polys = [this];
+
+ for(var i=0; i maxlevel){
+ console.warn("quickDecomp: max level ("+maxlevel+") reached.");
+ return result;
+ }
+
+ for (var i = 0; i < this.vertices.length; ++i) {
+ if (poly.isReflex(i)) {
+ reflexVertices.push(poly.vertices[i]);
+ upperDist = lowerDist = Number.MAX_VALUE;
+
+
+ for (var j = 0; j < this.vertices.length; ++j) {
+ if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge
+ p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection
+ if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < lowerDist) { // keep only the closest intersection
+ lowerDist = d;
+ lowerInt = p;
+ lowerIndex = j;
+ }
+ }
+ }
+ if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1));
+ if (Point.left(poly.at(i - 1), poly.at(i), p)) {
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < upperDist) {
+ upperDist = d;
+ upperInt = p;
+ upperIndex = j;
+ }
+ }
+ }
+ }
+
+ // if there are no vertices to connect to, choose a point in the middle
+ if (lowerIndex == (upperIndex + 1) % this.vertices.length) {
+ //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")");
+ p[0] = (lowerInt[0] + upperInt[0]) / 2;
+ p[1] = (lowerInt[1] + upperInt[1]) / 2;
+ steinerPoints.push(p);
+
+ if (i < upperIndex) {
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly, i, upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ if (lowerIndex != 0){
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
+ upperPoly.append(poly,lowerIndex,poly.vertices.length);
+ }
+ //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
+ lowerPoly.append(poly,i,poly.vertices.length);
+ }
+ //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly,0,upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
+ upperPoly.append(poly,lowerIndex,i+1);
+ }
+ } else {
+ // connect to the closest point within the triangle
+ //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n");
+
+ if (lowerIndex > upperIndex) {
+ upperIndex += this.vertices.length;
+ }
+ closestDist = Number.MAX_VALUE;
+
+ if(upperIndex < lowerIndex){
+ return result;
+ }
+
+ for (var j = lowerIndex; j <= upperIndex; ++j) {
+ if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ d = Point.sqdist(poly.at(i), poly.at(j));
+ if (d < closestDist) {
+ closestDist = d;
+ closestIndex = j % this.vertices.length;
+ }
+ }
+ }
+
+ if (i < closestIndex) {
+ lowerPoly.append(poly,i,closestIndex+1);
+ if (closestIndex != 0){
+ upperPoly.append(poly,closestIndex,v.length);
+ }
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ lowerPoly.append(poly,i,v.length);
+ }
+ lowerPoly.append(poly,0,closestIndex+1);
+ upperPoly.append(poly,closestIndex,i+1);
+ }
+ }
+
+ // solve smallest poly first
+ if (lowerPoly.vertices.length < upperPoly.vertices.length) {
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ } else {
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ }
+
+ return result;
+ }
+ }
+ result.push(this);
+
+ return result;
+};
+
+/**
+ * Remove collinear points in the polygon.
+ * @method removeCollinearPoints
+ * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision.
+ * @return {Number} The number of points removed
+ */
+Polygon.prototype.removeCollinearPoints = function(precision){
+ var num = 0;
+ for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){
+ if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){
+ // Remove the middle point
+ this.vertices.splice(i%this.vertices.length,1);
+ i--; // Jump one point forward. Otherwise we may get a chain removal
+ num++;
+ }
+ }
+ return num;
+};
+
+},{"./Line":1,"./Point":2,"./Scalar":4}],4:[function(require,module,exports){
+module.exports = Scalar;
+
+/**
+ * Scalar functions
+ * @class Scalar
+ */
+function Scalar(){}
+
+/**
+ * Check if two scalars are equal
+ * @static
+ * @method eq
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} [precision]
+ * @return {Boolean}
+ */
+Scalar.eq = function(a,b,precision){
+ precision = precision || 0;
+ return Math.abs(a-b) < precision;
+};
+
+},{}],5:[function(require,module,exports){
+module.exports = {
+ Polygon : require("./Polygon"),
+ Point : require("./Point"),
+};
+
+},{"./Point":2,"./Polygon":3}]},{},[5])
+(5)
+});
+;
\ No newline at end of file
diff --git a/src/body/Body.js b/src/body/Body.js
index 9f3db17..3092182 100644
--- a/src/body/Body.js
+++ b/src/body/Body.js
@@ -563,6 +563,9 @@ var Body = {};
* @return {}
*/
var _totalProperties = function(body) {
+ // https://ecourses.ou.edu/cgi-bin/ebook.cgi?doc=&topic=st&chap_sec=07.2&page=theory
+ // http://output.to/sideway/default.asp?qno=121100087
+
var properties = {
mass: 0,
area: 0,
@@ -576,10 +579,10 @@ var Body = {};
properties.mass += part.mass;
properties.area += part.area;
properties.inertia += part.inertia;
- Vector.add(properties.centre, part.position, properties.centre);
+ properties.centre = Vector.add(properties.centre, Vector.mult(part.position, part.mass));
}
- properties.centre = Vector.div(properties.centre, body.parts.length - 1);
+ properties.centre = Vector.div(properties.centre, properties.mass);
return properties;
};
diff --git a/src/factory/Bodies.js b/src/factory/Bodies.js
index 25d2aa2..bc6813e 100644
--- a/src/factory/Bodies.js
+++ b/src/factory/Bodies.js
@@ -93,7 +93,7 @@ var Bodies = {};
* @param {number} y
* @param {number} radius
* @param {object} [options]
- * @param {number} maxSides
+ * @param {number} [maxSides]
* @return {body} A new circle body
*/
Bodies.circle = function(x, y, radius, options, maxSides) {
@@ -161,4 +161,100 @@ var Bodies = {};
return Body.create(Common.extend({}, polygon, options));
};
+ /**
+ * Creates a body using the supplied vertices.
+ * If the vertices are not convex, they will be decomposed if [poly-decomp.js](https://github.com/schteppe/poly-decomp.js) is available.
+ * If the vertices can not be decomposed, the function will use the convex hull.
+ * By default the decomposition will discard collinear edges (to improve performance).
+ * The options parameter is an object that specifies any properties you wish to override the defaults.
+ * See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object.
+ * @method fromVertices
+ * @param {number} x
+ * @param {number} y
+ * @param [vector] vertices
+ * @param {object} [options]
+ * @param {bool} [removeCollinear=true]
+ * @return {body}
+ */
+ Bodies.fromVertices = function(x, y, vertices, options, removeCollinear) {
+ var canDecompose = true,
+ body,
+ i;
+
+ options = options || {};
+ removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : true;
+
+ if (Vertices.isConvex(vertices)) {
+ // vertices are convex, so just create a body normally
+ body = {
+ position: { x: x, y: y },
+ vertices: vertices
+ };
+
+ return Body.create(Common.extend({}, body, options));
+ }
+
+ // check for poly-decomp.js
+ if (!window.decomp) {
+ Common.log('Bodies.fromVertices: poly-decomp.js required. Could not decompose vertices. Fallback to convex hull.', 'warn');
+ canDecompose = false;
+ }
+
+ // initialise a decomposition
+ var concave = new decomp.Polygon();
+ for (i = 0; i < vertices.length; i++) {
+ concave.vertices.push([vertices[i].x, vertices[i].y]);
+ }
+
+ // check for complexity
+ if (!concave.isSimple()) {
+ Common.log('Bodies.fromVertices: Non-simple polygons are not supported. Could not decompose vertices. Fallback to convex hull.', 'warn');
+ canDecompose = false;
+ }
+
+ // try to decompose
+ if (canDecompose) {
+ // vertices are concave and simple, we can decompose into parts
+ concave.makeCCW();
+ if (removeCollinear)
+ concave.removeCollinearPoints(0.001);
+
+ var decomposed = concave.quickDecomp(),
+ parts = [];
+
+ // for each decomposed chunk
+ for (i = 0; i < decomposed.length; i++) {
+ var chunk = decomposed[i],
+ chunkVertices = [];
+
+ // convert vertices into the correct structure
+ for (var j = 0; j < chunk.vertices.length; j++) {
+ chunkVertices.push({ x: chunk.vertices[j][0], y: chunk.vertices[j][1] });
+ }
+
+ // create a compound part
+ parts.push(
+ Body.create({
+ position: Vertices.centre(chunkVertices),
+ vertices: chunkVertices
+ })
+ );
+ }
+
+ // create the parent body to be returned, that contains generated compound parts
+ body = Body.create(Common.extend({}, { parts: parts }, options));
+ Body.setPosition(body, { x: x, y: y });
+
+ return body;
+ } else {
+ // fallback to convex hull when decomposition is not possible
+ body = {
+ position: { x: x, y: y },
+ vertices: Vertices.hull(vertices)
+ };
+
+ return Body.create(Common.extend({}, body, options));
+ }
+ };
+
})();
diff --git a/src/geometry/Vertices.js b/src/geometry/Vertices.js
index bfa7cf5..0c06a78 100644
--- a/src/geometry/Vertices.js
+++ b/src/geometry/Vertices.js
@@ -56,7 +56,7 @@ var Vertices = {};
* @return {vertices} vertices
*/
Vertices.fromPath = function(path, body) {
- var pathPattern = /L\s*([\-\d\.]*)\s*([\-\d\.]*)/ig,
+ var pathPattern = /L?\s*([\-\d\.]+)\s*([\-\d\.]+)\s*,?/ig,
points = [];
path.replace(pathPattern, function(match, x, y) {
@@ -330,15 +330,58 @@ var Vertices = {};
* @return {vertices} vertices
*/
Vertices.clockwiseSort = function(vertices) {
- var mean = Vertices.mean(vertices);
+ var centre = Vertices.mean(vertices);
vertices.sort(function(vertexA, vertexB) {
- return Vector.angle(mean, vertexA) - Vector.angle(mean, vertexB);
+ return Vector.angle(centre, vertexA) - Vector.angle(centre, vertexB);
});
return vertices;
};
+ /**
+ * Returns true if the vertices form a convex shape (vertices must be in clockwise order).
+ * @method isConvex
+ * @param {vertices} vertices
+ * @return {bool} `true` if the `vertices` are convex, `false` if not (or `null` if not computable).
+ */
+ Vertices.isConvex = function(vertices) {
+ // http://paulbourke.net/geometry/polygonmesh/
+
+ var flag = 0,
+ n = vertices.length,
+ i,
+ j,
+ k,
+ z;
+
+ if (n < 3)
+ return null;
+
+ for (i = 0; i < n; i++) {
+ j = (i + 1) % n;
+ k = (i + 2) % n;
+ z = (vertices[j].x - vertices[i].x) * (vertices[k].y - vertices[j].y);
+ z -= (vertices[j].y - vertices[i].y) * (vertices[k].x - vertices[j].x);
+
+ if (z < 0) {
+ flag |= 1;
+ } else if (z > 0) {
+ flag |= 2;
+ }
+
+ if (flag === 3) {
+ return false;
+ }
+ }
+
+ if (flag !== 0){
+ return true;
+ } else {
+ return null;
+ }
+ };
+
/**
* Returns the convex hull of the input vertices as a new array of points.
* @method hull