求任意凸多边形的重心, 面积与平面图形的转动惯量
已知一个三角形的三个顶点的坐标, 三角形的重心坐标的公式如下:
getTriangleGravity: function (nX1, nY1, nX2, nY2, nX3, nY3) { return new Vector2D((nX1 + nX2 + nX3) / 3, (nY1 + nY2 + nY3) / 3); }
求其面积:
getTriangleArea: function (nX1, nY1, nX2, nY2, nX3, nY3) { return ((nX2 - nX1) * (nY3 - nY1) - (nX3 - nX1) * (nY2 - nY1)) / 2; }
这个公式根据三个顶点是顺时针还是逆时针排序可能求得面积是负数, 必要时可以取绝对值.
对于一个凸多边形, 取其中一个顶点poly[0], 可以依次与poly[i], poly[i+1]组成三角形, 于是可以把任意一个凸多边形拆成多个三角形. 于是可以如下计算凸多边形的重心和面积:
getGravityAndArea: function () { var len = this.length - 1, x0 = this[0].x, y0 = this[0].y, x1, y1, x2, y2, xSum = 0, ySum = 0, areaSum = 0; for (var i = 1; i < len; ++i) { x1 = this[i].x; y1 = this[i].y; x2 = this[i + 1].x; y2 = this[i + 1].y; var g = getTriangleGravity(x0, y0, x1, y1, x2, y2), area = getTriangleArea(x0, y0, x1, y1, x2, y2); xSum += g.x * area; ySum += g.y * area; areaSum += area; } return { gravity: new P.Vector2D(xSum / areaSum, ySum / areaSum), area: areaSum }; }
每个小三角形求得重心后乘以其面积作为权重, 最后再除以总面积当作多边形的重心坐标.
判断一个多边形是否为凸多边形, 可以依次取连续的两条边求其向量的叉乘, 判断叉乘结果的符号是否全部相同.
isConvex: function () { var prod0, vec1, vec2, prodc, i, l; vec1 = this[1].sub(this[0]); vec2 = this[2].sub(this[1]); prod0 = vec1.cross(vec2); for (i = 1, l = this.length; i + 2 < l; ++i) { vec1 = this[i + 1].sub(this[i]); vec2 = this[i + 2].sub(this[i + 1]); prodc = vec1.cross(vec2); if (prodc * prod0 < 0) { return false; } } vec1 = this[i + 1].sub(this[i]); vec2 = this[0].sub(this[i + 1]); prodc = vec1.cross(vec2); return prodc * prod0 >= 0; }
要求平面多边形和圆形的转动惯量, 可以参考这条维基上求绕着原点旋转的圆形与多边形板子的转动惯量公式.
getPolygonInertia: function (poly, nMass, oCentroid) { var len = poly.length, i, p1, p2, cross, sum1 = 0, sum2 = 0; for (i = 0; i < len; ++i) { p1 = poly[i].sub(oCentroid); p2 = poly[(i + 1) % len].sub(oCentroid); cross = p1.cross(p2); sum1 += cross * (p1.dot(p1) + p1.dot(p2) + p2.dot(p2)); sum2 += cross; } return nMass / 6 * sum1 / sum2; }
getCircleInertia: function (cir, nMass) { return nMass * cir.radius * cir.radius / 2 }