Made joints easier to create
[qml-box2d:anubhavtiwaris-qml-box2d.git] / box2dbody.cpp
1 /*
2  * Box2D QML plugin
3  * Copyright (C) 2010 Nokia Corporation
4  *
5  * This file is part of the Box2D QML plugin.
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or (at
10  * your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  * License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library;  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "box2dbody.h"
22
23 #include "box2dfixture.h"
24 #include "box2dworld.h"
25
26 #include <cmath>
27
28 Box2DBody::Box2DBody(QDeclarativeItem *parent) :
29     QDeclarativeItem(parent),
30     mBody(0),
31     mWorld(0),
32     mLinearDamping(0.0f),
33     mAngularDamping(0.0f),
34     mBodyType(Dynamic),
35     mBullet(false),
36     mSleepingAllowed(true),
37     mFixedRotation(false),
38     mActive(true),
39     mSynchronizing(false),
40     mInitializePending(false)
41 {
42     setTransformOrigin(TopLeft);
43     connect(this, SIGNAL(rotationChanged()), SLOT(onRotationChanged()));
44 }
45
46 Box2DBody::~Box2DBody()
47 {
48 }
49
50 void Box2DBody::setLinearDamping(qreal linearDamping)
51 {
52     if (mLinearDamping == linearDamping)
53         return;
54
55     mLinearDamping = linearDamping;
56     if (mBody)
57         mBody->SetLinearDamping(linearDamping);
58     emit linearDampingChanged();
59 }
60
61 void Box2DBody::setAngularDamping(qreal angularDamping)
62 {
63     if (mAngularDamping == angularDamping)
64         return;
65
66     mAngularDamping = angularDamping;
67     if (mBody)
68         mBody->SetAngularDamping(angularDamping);
69     emit angularDampingChanged();
70 }
71
72 void Box2DBody::setBodyType(BodyType bodyType)
73 {
74     if (mBodyType == bodyType)
75         return;
76
77     mBodyType = bodyType;
78     if (mBody)
79         mBody->SetType(static_cast<b2BodyType>(bodyType));
80     emit bodyTypeChanged();
81 }
82
83 void Box2DBody::setBullet(bool bullet)
84 {
85     if (mBullet == bullet)
86         return;
87
88     mBullet = bullet;
89     if (mBody)
90         mBody->SetBullet(bullet);
91     emit bulletChanged();
92 }
93
94 void Box2DBody::setSleepingAllowed(bool allowed)
95 {
96     if (mSleepingAllowed == allowed)
97         return;
98
99     mSleepingAllowed = allowed;
100     if (mBody)
101         mBody->SetSleepingAllowed(allowed);
102     emit sleepingAllowedChanged();
103 }
104
105 void Box2DBody::setFixedRotation(bool fixedRotation)
106 {
107     if (mFixedRotation == fixedRotation)
108         return;
109
110     mFixedRotation = fixedRotation;
111     if (mBody)
112         mBody->SetFixedRotation(fixedRotation);
113     emit fixedRotationChanged();
114 }
115
116 void Box2DBody::setActive(bool active)
117 {
118     if (mActive == active)
119         return;
120
121     mActive = active;
122     if (mBody)
123         mBody->SetActive(active);
124 }
125
126 void Box2DBody::setLinearVelocity(const QPointF &linearVelocity)
127 {
128     if (mLinearVelocity == linearVelocity)
129         return;
130
131     mLinearVelocity = linearVelocity;
132     if (mBody)
133         mBody->SetLinearVelocity(b2Vec2(mLinearVelocity.x() / scaleRatio,
134                                         -mLinearVelocity.y() / scaleRatio));
135     emit linearVelocityChanged();
136 }
137
138 QDeclarativeListProperty<Box2DFixture> Box2DBody::fixtures()
139 {
140     return QDeclarativeListProperty<Box2DFixture>(this, 0,
141                                                   &Box2DBody::append_fixture);
142 }
143
144 void Box2DBody::append_fixture(QDeclarativeListProperty<Box2DFixture> *list,
145                                Box2DFixture *fixture)
146 {
147     Box2DBody *body = static_cast<Box2DBody*>(list->object);
148     fixture->setParentItem(body);
149     body->mFixtures.append(fixture);
150 }
151
152 void Box2DBody::initialize(b2World *world)
153 {
154     mWorld = world;
155
156     if (!isComponentComplete()) {
157         // When components are created dynamically, they get their parent
158         // assigned before they have been completely initialized. In that case
159         // we need to delay initialization.
160         mInitializePending = true;
161         return;
162     }
163
164     b2BodyDef bodyDef;
165     bodyDef.type = static_cast<b2BodyType>(mBodyType);
166     bodyDef.position.Set(x() / scaleRatio, -y() / scaleRatio);
167     bodyDef.angle = -(rotation() * (2 * b2_pi)) / 360.0;
168     bodyDef.linearDamping = mLinearDamping;
169     bodyDef.angularDamping = mAngularDamping;
170     bodyDef.bullet = mBullet;
171     bodyDef.allowSleep = mSleepingAllowed;
172     bodyDef.fixedRotation = mFixedRotation;
173
174     mBody = world->CreateBody(&bodyDef);
175     mInitializePending = false;
176
177     foreach (Box2DFixture *fixture, mFixtures)
178         fixture->createFixture(mBody);
179
180     emit bodyCreated();
181 }
182
183 /**
184  * Synchronizes the state of this body with the internal Box2D state.
185  */
186 void Box2DBody::synchronize()
187 {
188     Q_ASSERT(mBody);
189     mSynchronizing = true;
190
191     const b2Vec2 position = mBody->GetPosition();
192     const float32 angle = mBody->GetAngle();
193
194     const qreal newX = position.x * scaleRatio;
195     const qreal newY = -position.y * scaleRatio;
196     const qreal newRotation = -(angle * 360.0) / (2 * b2_pi);
197
198     // Do fuzzy comparisions to avoid small inaccuracies causing repaints
199     if (!qFuzzyCompare(x(), newX) || !qFuzzyCompare(y(), newY))
200         setPos(newX, newY);
201     if (!qFuzzyCompare(rotation(), newRotation))
202         setRotation(newRotation);
203
204     b2Vec2 linearVelocity = mBody->GetLinearVelocity();
205     setLinearVelocity(QPointF(linearVelocity.x * scaleRatio,
206                               -linearVelocity.y * scaleRatio));
207
208     mSynchronizing = false;
209 }
210
211 void Box2DBody::cleanup(b2World *world)
212 {
213     world->DestroyBody(mBody);
214     mBody = 0;
215     mWorld = 0;
216 }
217
218 void Box2DBody::componentComplete()
219 {
220     QDeclarativeItem::componentComplete();
221
222     if (mInitializePending)
223         initialize(mWorld);
224 }
225
226 b2Body *Box2DBody::body() const
227 {
228     return mBody;
229 }
230
231 void Box2DBody::geometryChanged(const QRectF &newGeometry,
232                                 const QRectF &oldGeometry)
233 {
234     if (!mSynchronizing && mBody) {
235         if (newGeometry.topLeft() != oldGeometry.topLeft()) {
236             const QPointF pos = newGeometry.topLeft();
237             mBody->SetTransform(b2Vec2(pos.x() / scaleRatio,
238                                        -pos.y() / scaleRatio),
239                                 mBody->GetAngle());
240         }
241     }
242
243     QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
244 }
245
246 void Box2DBody::onRotationChanged()
247 {
248     if (!mSynchronizing && mBody) {
249         mBody->SetTransform(mBody->GetPosition(),
250                             (rotation() * 2 * b2_pi) / -360.0);
251     }
252 }
253
254 void Box2DBody::applyLinearImpulse(const QPointF &impulse,
255                                    const QPointF &point)
256 {
257     if (mBody) {
258         mBody->ApplyLinearImpulse(b2Vec2(impulse.x() / scaleRatio,
259                                          -impulse.y() / scaleRatio),
260                                   b2Vec2(point.x() / scaleRatio,
261                                          -point.y() / scaleRatio));
262     }
263 }
264
265 QPointF Box2DBody::getWorldCenter() const
266 {
267     QPointF worldCenter;
268     if (mBody) {
269         const b2Vec2 &center = mBody->GetWorldCenter();
270         worldCenter.setX(center.x * scaleRatio);
271         worldCenter.setY(-center.y * scaleRatio);
272     }
273     return worldCenter;
274 }