File indexing completed on 2024-03-24 15:14:07

0001 /*
0002  * box2dfixture.cpp
0003  * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
0004  * Copyright (c) 2011 Daker Fernandes Pinheiro <daker.pinheiro@openbossa.org>
0005  * Copyright (c) 2011 Alessandro Portale <alessandro.portale@nokia.com>
0006  *
0007  * This file is part of the Box2D QML plugin.
0008  *
0009  * This software is provided 'as-is', without any express or implied warranty.
0010  * In no event will the authors be held liable for any damages arising from
0011  * the use of this software.
0012  *
0013  * Permission is granted to anyone to use this software for any purpose,
0014  * including commercial applications, and to alter it and redistribute it
0015  * freely, subject to the following restrictions:
0016  *
0017  * 1. The origin of this software must not be misrepresented; you must not
0018  *    claim that you wrote the original software. If you use this software in
0019  *    a product, an acknowledgment in the product documentation would be
0020  *    appreciated but is not required.
0021  *
0022  * 2. Altered source versions must be plainly marked as such, and must not be
0023  *    misrepresented as being the original software.
0024  *
0025  * 3. This notice may not be removed or altered from any source distribution.
0026  */
0027 
0028 #include "box2dfixture.h"
0029 
0030 #include "box2dbody.h"
0031 #include "box2dworld.h"
0032 
0033 #include <QDebug>
0034 
0035 #include "Common/b2Math.h"
0036 
0037 Box2DFixture::Box2DFixture(QObject *parent) :
0038     QObject(parent),
0039     mFixture(0),
0040     mBody(0)
0041 {
0042     mFixtureDef.userData = this;
0043 }
0044 
0045 float Box2DFixture::density() const
0046 {
0047     return mFixtureDef.density;
0048 }
0049 
0050 void Box2DFixture::setDensity(float density)
0051 {
0052     if (mFixtureDef.density == density)
0053         return;
0054 
0055     mFixtureDef.density = density;
0056     if (mFixture)
0057         mFixture->SetDensity(density);
0058     emit densityChanged();
0059 }
0060 
0061 float Box2DFixture::friction() const
0062 {
0063     return mFixtureDef.friction;
0064 }
0065 
0066 void Box2DFixture::setFriction(float friction)
0067 {
0068     if (mFixtureDef.friction == friction)
0069         return;
0070 
0071     mFixtureDef.friction = friction;
0072     if (mFixture)
0073         mFixture->SetFriction(friction);
0074     emit frictionChanged();
0075 }
0076 
0077 float Box2DFixture::restitution() const
0078 {
0079     return mFixtureDef.restitution;
0080 }
0081 
0082 void Box2DFixture::setRestitution(float restitution)
0083 {
0084     if (mFixtureDef.restitution == restitution)
0085         return;
0086 
0087     mFixtureDef.restitution = restitution;
0088     if (mFixture)
0089         mFixture->SetRestitution(restitution);
0090     emit restitutionChanged();
0091 }
0092 
0093 bool Box2DFixture::isSensor() const
0094 {
0095     return mFixtureDef.isSensor;
0096 }
0097 
0098 void Box2DFixture::setSensor(bool sensor)
0099 {
0100     if (mFixtureDef.isSensor == sensor)
0101         return;
0102 
0103     mFixtureDef.isSensor = sensor;
0104     if (mFixture)
0105         mFixture->SetSensor(sensor);
0106     emit sensorChanged();
0107 }
0108 
0109 Box2DFixture::CategoryFlags Box2DFixture::categories() const
0110 {
0111     return CategoryFlags(mFixtureDef.filter.categoryBits);
0112 }
0113 
0114 void Box2DFixture::setCategories(CategoryFlags layers)
0115 {
0116     if (mFixtureDef.filter.categoryBits == layers)
0117         return;
0118 
0119     mFixtureDef.filter.categoryBits = layers;
0120     if (mFixture)
0121         mFixture->SetFilterData(mFixtureDef.filter);
0122 
0123     emit categoriesChanged();
0124 }
0125 
0126 Box2DFixture::CategoryFlags Box2DFixture::collidesWith() const
0127 {
0128     return CategoryFlags(mFixtureDef.filter.maskBits);
0129 }
0130 
0131 void Box2DFixture::setCollidesWith(CategoryFlags layers)
0132 {
0133     if (mFixtureDef.filter.maskBits == layers)
0134         return;
0135 
0136     mFixtureDef.filter.maskBits = layers;
0137     if (mFixture)
0138         mFixture->SetFilterData(mFixtureDef.filter);
0139 
0140     emit collidesWithChanged();
0141 }
0142 
0143 int Box2DFixture::groupIndex() const
0144 {
0145     return mFixtureDef.filter.groupIndex;
0146 }
0147 
0148 void Box2DFixture::setGroupIndex(int groupIndex)
0149 {
0150     if (mFixtureDef.filter.groupIndex == groupIndex)
0151         return;
0152 
0153     mFixtureDef.filter.groupIndex = groupIndex;
0154     if (mFixture)
0155         mFixture->SetFilterData(mFixtureDef.filter);
0156 
0157     emit groupIndexChanged();
0158 }
0159 
0160 void Box2DFixture::initialize(Box2DBody *body)
0161 {
0162     mBody = body;
0163     b2Shape *shape = createShape();
0164     if (!shape)
0165         return;
0166 
0167     mFixtureDef.shape = shape;
0168     mFixture = body->body()->CreateFixture(&mFixtureDef);
0169     delete shape;
0170 }
0171 
0172 void Box2DFixture::recreateFixture()
0173 {
0174     if (!mBody)
0175         return;
0176     if (mFixture)
0177         mBody->body()->DestroyFixture(mFixture);
0178     initialize(mBody);
0179 }
0180 
0181 Box2DBody *Box2DFixture::getBody() const
0182 {
0183     return mBody;
0184 }
0185 
0186 //=================== BOX =======================
0187 
0188 void Box2DBox::setX(qreal x)
0189 {
0190     if (mPosition.x() == x)
0191         return;
0192     mPosition.setX(x);
0193     recreateFixture();
0194     emit xChanged();
0195 }
0196 
0197 void Box2DBox::setY(qreal y)
0198 {
0199     if (mPosition.y() == y)
0200         return;
0201     mPosition.setY(y);
0202     recreateFixture();
0203     emit yChanged();
0204 }
0205 
0206 void Box2DBox::setWidth(qreal width)
0207 {
0208     if (mSize.width() == width)
0209         return;
0210     mSize.setWidth(width);
0211     recreateFixture();
0212     emit widthChanged();
0213 }
0214 
0215 void Box2DBox::setHeight(qreal height)
0216 {
0217     if (mSize.height() == height)
0218         return;
0219     mSize.setHeight(height);
0220     recreateFixture();
0221     emit heightChanged();
0222 }
0223 
0224 void Box2DBox::setRotation(qreal rotation)
0225 {
0226     if (mRotation == rotation)
0227         return;
0228     mRotation = rotation;
0229     recreateFixture();
0230     emit rotationChanged();
0231 }
0232 
0233 b2Shape *Box2DBox::createShape()
0234 {
0235     const qreal halfWidth = width() * 0.5;
0236     const qreal halfHeight = height() * 0.5;
0237     const QPointF center(x() + halfWidth,
0238                          y() + halfHeight);
0239 
0240     b2PolygonShape *shape = new b2PolygonShape;
0241     shape->SetAsBox(b2Max(mBody->world()->toMeters(halfWidth), b2_linearSlop),
0242                     b2Max(mBody->world()->toMeters(halfHeight), b2_linearSlop),
0243                     mBody->world()->toMeters(center),
0244                     toRadians(rotation()));
0245 
0246     return shape;
0247 }
0248 
0249 //=================== CIRCLE =======================
0250 
0251 void Box2DCircle::setX(qreal x)
0252 {
0253     if (mPosition.x() == x)
0254         return;
0255     mPosition.setX(x);
0256     recreateFixture();
0257     emit xChanged();
0258 }
0259 
0260 void Box2DCircle::setY(qreal y)
0261 {
0262     if (mPosition.y() == y)
0263         return;
0264     mPosition.setY(y);
0265     recreateFixture();
0266     emit yChanged();
0267 }
0268 
0269 void Box2DCircle::setRadius(float radius)
0270 {
0271     if (mRadius == radius)
0272         return;
0273     mRadius = radius;
0274     recreateFixture();
0275     emit radiusChanged();
0276 }
0277 
0278 b2Shape *Box2DCircle::createShape()
0279 {
0280     b2CircleShape *shape = new b2CircleShape;
0281 
0282     shape->m_radius = mBody->world()->toMeters(radius());
0283     shape->m_p = mBody->world()->toMeters(position() + QPointF(radius(), radius()));
0284 
0285     return shape;
0286 }
0287 
0288 //=================== POLYGON =======================
0289 
0290 void Box2DPolygon::setVertices(const QVariantList &vertices)
0291 {
0292     if (vertices == mVertices)
0293         return;
0294 
0295     mVertices = vertices;
0296     recreateFixture();
0297     emit verticesChanged();
0298 }
0299 
0300 b2Shape *Box2DPolygon::createShape()
0301 {
0302     const int count = mVertices.length();
0303     if (count < 2 || count > b2_maxPolygonVertices) {
0304         qWarning() << "Polygon: Invalid number of vertices:" << count;
0305         return 0;
0306     }
0307 
0308     QScopedArrayPointer<b2Vec2> vertices(new b2Vec2[count]);
0309 
0310     for (int i = 0; i < count; ++i) {
0311         vertices[i] = mBody->world()->toMeters(mVertices.at(i).toPointF());
0312 
0313         if (i > 0) {
0314             if (b2DistanceSquared(vertices[i - 1], vertices[i]) <= b2_linearSlop * b2_linearSlop) {
0315                 qWarning() << "Polygon: vertices are too close together";
0316                 return 0;
0317             }
0318         }
0319     }
0320 
0321     b2PolygonShape *shape = new b2PolygonShape;
0322     shape->Set(vertices.data(), count);
0323 
0324     return shape;
0325 }
0326 
0327 //=================== CHAIN =======================
0328 
0329 Box2DChain::Box2DChain(QQuickItem *parent) :
0330     Box2DFixture(parent),
0331     mLoop(false),
0332     mPrevVertexFlag(false),
0333     mNextVertexFlag(false)
0334 {
0335 }
0336 
0337 void Box2DChain::setVertices(const QVariantList &vertices)
0338 {
0339     if (vertices == mVertices)
0340         return;
0341 
0342     mVertices = vertices;
0343     recreateFixture();
0344     emit verticesChanged();
0345 }
0346 
0347 void Box2DChain::setLoop(bool loop)
0348 {
0349     if (mLoop == loop)
0350         return;
0351 
0352     mLoop = loop;
0353     recreateFixture();
0354     emit loopChanged();
0355 }
0356 
0357 void Box2DChain::setPrevVertex(const QPointF &prevVertex)
0358 {
0359     if (mPrevVertexFlag && mPrevVertex == prevVertex)
0360         return;
0361 
0362     mPrevVertex = prevVertex;
0363     mPrevVertexFlag = true;
0364     recreateFixture();
0365     emit prevVertexChanged();
0366 }
0367 
0368 void Box2DChain::setNextVertex(const QPointF &nextVertex)
0369 {
0370     if (mNextVertexFlag && mNextVertex == nextVertex)
0371         return;
0372 
0373     mNextVertex = nextVertex;
0374     mNextVertexFlag = true;
0375     recreateFixture();
0376     emit nextVertexChanged();
0377 }
0378 
0379 b2Shape *Box2DChain::createShape()
0380 {
0381     const int count = mVertices.length();
0382 
0383     if (count < 2 || (mLoop && count < 3)) {
0384         qWarning() << "Chain: Invalid number of vertices:" << count;
0385         return 0;
0386     }
0387 
0388     QScopedArrayPointer<b2Vec2> vertices(new b2Vec2[count]);
0389 
0390     for (int i = 0; i < count; ++i) {
0391         vertices[i] = mBody->world()->toMeters(mVertices.at(i).toPointF());
0392 
0393         if (i > 0) {
0394             if (b2DistanceSquared(vertices[i - 1], vertices[i]) <= b2_linearSlop * b2_linearSlop) {
0395                 qWarning() << "Chain: vertices are too close together";
0396                 return 0;
0397             }
0398         }
0399     }
0400 
0401     b2ChainShape *shape = new b2ChainShape;
0402     if (mLoop) {
0403         shape->CreateLoop(vertices.data(), count);
0404     } else {
0405         shape->CreateChain(vertices.data(), count);
0406 
0407         if (mPrevVertexFlag)
0408             shape->SetPrevVertex(mBody->world()->toMeters(mPrevVertex));
0409         if (mNextVertexFlag)
0410             shape->SetNextVertex(mBody->world()->toMeters(mNextVertex));
0411     }
0412 
0413     return shape;
0414 }
0415 
0416 //=================== EDGE =======================
0417 
0418 void Box2DEdge::setVertices(const QVariantList &vertices)
0419 {
0420     if (vertices == mVertices)
0421         return;
0422 
0423     mVertices = vertices;
0424     recreateFixture();
0425     emit verticesChanged();
0426 }
0427 
0428 b2Shape *Box2DEdge::createShape()
0429 {
0430     const int count = mVertices.length();
0431     if (count != 2) {
0432         qWarning() << "Edge: Invalid number of vertices:" << count;
0433         return 0;
0434     }
0435     const b2Vec2 vertex1 = mBody->world()->toMeters(mVertices.at(0).toPointF());
0436     const b2Vec2 vertex2 = mBody->world()->toMeters(mVertices.at(1).toPointF());
0437     if (b2DistanceSquared(vertex1, vertex2) <= b2_linearSlop * b2_linearSlop) {
0438         qWarning() << "Edge: vertices are too close together";
0439         return 0;
0440     }
0441     b2EdgeShape *shape = new b2EdgeShape;
0442     shape->Set(vertex1, vertex2);
0443 
0444     return shape;
0445 }