#include "polygon.h" #include #include #include #include using namespace wgd; Polygon::Polygon(const wgd::InstanceID &inst) : Body(inst), m_verticesChanged(true), m_moved_polygon(true), xMin(0), xMax(0), yMin(0), yMax(0), count(0), area(1) { if (property("vertices") == Null) { property("vertices", DB::createObject()); } OID v = property("vertices"); if (v["count"] == Null) { v.set("count", 0); } else { count = v["count"]; } DOSTE_REGISTER(100, v("count")); DOSTE_REGISTER(102, getID()(ix::x)); DOSTE_REGISTER(103, getID()(ix::y)); DOSTE_REGISTER(104, getID()(ix::roll)); } void Polygon::vertexAdded (WGD_HANDLER) { int before = oldvalue; count = value; m_verticesChanged = true; m_moved_polygon = true; for (int i = before; i < count; i++) { OID v = property("vertices")[i]; if (v == Null) { v = DB::createObject(); property("vertices").set(i, v); } DOSTE_REGISTER(101, v(ix::x)); DOSTE_REGISTER(101, v(ix::y)); } } void Polygon::vertexChanged (WGD_HANDLER) { m_verticesChanged = true; m_moved_polygon = true; } void Polygon::positionChanged (WGD_HANDLER) { m_moved_polygon = true; } bool Polygon::contains (const Vector2D& p) { checkCache(); if (count < 3) { return false; } for (int i = 0; i < count; i++) { if (getNormal(i).dot(p - getVertex(i)) > 0) { return false; } } return true; } Vector2D Polygon::getExtremePoint (const Vector2D& direction, int& index) const { index = 0; wgd::Vector2D point = vertices[0]; float val = point.dot(direction); for (int i = 1; i < count; i++) { float x = vertices[i].dot(direction); if (x > val) { val = x; index = i; point = vertices[i]; } } return point + direction * getRadius(); } void Polygon::addVertex (float x, float y) { OID vertices = property("vertices"); int count = (int) vertices["count"]; if (count > 0) { Vector2D prev = getVertex(count - 1); if (prev.x == x && prev.y == y) { return; } } Matrix rotation; Matrix translation; rotation.rotate(-angle()); translation.translate(-position()); Vector2D point(x, y); point = rotation * translation * point; OID v = DB::createObject(); v.set(ix::x, point.x); v.set(ix::y, point.y); vertices.set(count, v); vertices.set("count", count + 1); } void Polygon::generateCache () { checkVertexCache(); m_moved_polygon = false; if (count == 0) { vertices.resize(1); vertices[0] = position(); return; } OID verticesOID = property("vertices"); Matrix matrix = localMatrix(); vertices.resize(count); for (int i = 0; i < count; i++) { vertices[i] = Vector2D(verticesOID[i][ix::x], verticesOID[i][ix::y]); vertices[i] = matrix * vertices[i]; } for (int i = 0; i < count; i++) { if (vertices[i].x < vertices[xMin].x) { xMin = i; } if (vertices[i].x > vertices[xMax].x) { xMax = i; } if (vertices[i].y < vertices[yMin].y) { yMin = i; } if (vertices[i].y > vertices[yMax].y) { yMax = i; } } } // returns twice the signed triangle area // assuming origin is in the bottom left corner // the result is < 0 if points are clockwise and > 0 if anticlockwise // a result of 0 means they are colinear inline float signedArea (const Vector2D& a, const Vector2D& b, const Vector2D& c) { return (a.x-b.x)*(b.y-c.y) - (b.x-c.x)*(a.y-b.y); } inline bool clockwise (const Vector2D& a, const Vector2D& b, const Vector2D& c) { return signedArea(a, b, c) < 0.00001f; } inline int convexHullNextPoint (const Vector2D *const points, const int count, const int last) { // candidate for the next point int next = 0; // check all points for (int i = 1; i < count; i++) { if (i == last) { continue; } if (clockwise(points[last], points[i], points[next])) { // next cannot be the next point, so replace it with i. next = i; } } return next; } void makeConvexHull (Vector2D* oldPoints, int count, Vector2D* newPoints, int& newCount) { // for fewer than 3 points, the output is the input if (count < 3) { newCount = count; for (int i = 0; i < count; i++) { newPoints[i] = oldPoints[i]; } return; } // here is just the index of the point we're currently working on unsigned int here = 0; // find the point with smallest x for (int i = 1; i < count; i++) { if (oldPoints[i].x < oldPoints[here].x) { here = i; } } newPoints[0] = oldPoints[here]; newCount = 0; // gift-wrapping algorithm while (true) { here = convexHullNextPoint(oldPoints, count, here); newCount++; if (oldPoints[here] == newPoints[0]) { break; } newPoints[newCount] = oldPoints[here]; } } void makeConvexHull (Vector2D* points, int& count) { Vector2D newPoints[count]; int newCount = count; makeConvexHull(points, count, newPoints, newCount); count = newCount; for (int i = 0; i < count; i++) { points[i] = newPoints[i]; } } void Polygon::remakeCOM () { m_verticesChanged = false; area = 1; momentOfInertia = Infinity; if (count == 0) { return; } else if (count == 1) { momentOfInertia = Infinity; } else if (count == 2) { momentOfInertia = 1; } Vector2D localPoints[count]; OID dbPoints = property("vertices"); for (int i = 0; i < count; i++) { localPoints[i] = Vector2D(dbPoints[i][ix::x], dbPoints[i][ix::y]); } makeConvexHull(localPoints, count); dbPoints.set("count", count); float xTotal = 0; float yTotal = 0; for (int i = 0; i < count; i++) { xTotal += localPoints[i].x; yTotal += localPoints[i].y; } xTotal /= count; yTotal /= count; if (xTotal != 0) { for (int i = 0; i < count; i++) { localPoints[i].x -= xTotal; } } if (yTotal != 0) { for (int i = 0; i < count; i++) { localPoints[i].y -= yTotal; } } for (int i = 0; i < count; i++) { dbPoints[i].set(ix::x, localPoints[i].x); dbPoints[i].set(ix::y, localPoints[i].y); } translate(Vector2D(xTotal, yTotal)); if (count < 3) { return; } area = 0; for (int i = 0; i < count; i++) { int j = i+1; if (j == count) { j = 0; } area -= (float)localPoints[i].x * (float)localPoints[j].y; area += (float)localPoints[j].x * (float)localPoints[i].y; } area /= 2; momentOfInertia = 0; float denom = 0; for (int i = 0; i < count; i++) { Vector3D a = localPoints[(i+1)%count]; Vector3D b = localPoints[i]; float l = (Vector3D::crossProduct(a, b)).length(); momentOfInertia += l * (a.dot(a) + a.dot(b) + b.dot(b)); denom += l; } momentOfInertia *= area / (6 * denom); } void Polygon::update() { checkCache(); Body::update(); } void Polygon::draw() { checkCache(); if (count == 1) { glColor3f(0, 0, 0); glBegin(GL_POINTS); glVertex3f(getX(), getY(), 0); glEnd(); return; } Colour c = getColour(); glColor3f(c.r(), c.g(), c.b()); glBegin(GL_POLYGON); for (int i = 0; i < count; i++) { glVertex3f(vertices[i].x, vertices[i].y, 0); } glEnd(); glColor3f(0, 0, 0); glBegin(GL_LINE_LOOP); for (int i = 0; i < count; i++) { glVertex3f(vertices[i].x, vertices[i].y, 0); } glEnd(); }