1 /*
2  * box2dworld.h
3  * Copyright (c) 2010 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4  * Copyright (c) 2011 Joonas Erkinheimo <joonas.erkinheimo@nokia.com>
5  * Copyright (c) 2012 Adriano Rezende <atdrez@gmail.com>
6  *
7  * This file is part of the Box2D QML plugin.
8  *
9  * This software is provided 'as-is', without any express or implied warranty.
10  * In no event will the authors be held liable for any damages arising from
11  * the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute it
15  * freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software in
19  *    a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #ifndef BOX2DWORLD_H
29 #define BOX2DWORLD_H
30 
31 #include <QAbstractAnimation>
32 #include <QQuickItem>
33 
34 #include <Box2D.h>
35 
36 class Box2DContact;
37 class Box2DFixture;
38 class Box2DJoint;
39 class Box2DWorld;
40 class Box2DRayCast;
41 class ContactListener;
42 class StepDriver;
43 
44 /**
45  * Small utility class to synchronize the stepping with the framerate.
46  */
47 class StepDriver : public QAbstractAnimation
48 {
49     Q_OBJECT
50 
51 public:
52     explicit StepDriver(Box2DWorld *world);
53 
54     int duration() const;
55 
56 protected:
57     void updateCurrentTime(int);
58 
59 private:
60     Box2DWorld *mWorld;
61 };
62 
63 
64 /**
65  * A property group for getting profiling data.
66  */
67 class Box2DProfile : public QObject
68 {
69     Q_OBJECT
70 
Q_PROPERTY(float step READ step CONSTANT)71     Q_PROPERTY(float step READ step CONSTANT)
72     Q_PROPERTY(float collide READ collide CONSTANT)
73     Q_PROPERTY(float solve READ solve CONSTANT)
74     Q_PROPERTY(float solveInit READ solveInit CONSTANT)
75     Q_PROPERTY(float solveVelocity READ solveVelocity CONSTANT)
76     Q_PROPERTY(float solvePosition READ solvePosition CONSTANT)
77     Q_PROPERTY(float broadphase READ broadphase CONSTANT)
78     Q_PROPERTY(float solveTOI READ solveTOI CONSTANT)
79     Q_PROPERTY(float synchronize READ synchronize CONSTANT)
80     Q_PROPERTY(float emitSignals READ emitSignals CONSTANT)
81 
82 public:
83     explicit Box2DProfile(b2World *world, QObject *parent = 0)
84         : QObject(parent)
85         , mWorld(world)
86     {}
87 
88     float step() const;
89     float collide() const;
90     float solve() const;
91     float solveInit() const;
92     float solveVelocity() const;
93     float solvePosition() const;
94     float broadphase() const;
95     float solveTOI() const;
96     float synchronize() const;
97     float emitSignals() const;
98 
99 private:
100     friend class Box2DWorld;
101 
102     b2World *mWorld;
103     float mSynchronize;
104     float mEmitSignals;
105 };
106 
107 
108 /**
109  * Wrapper class around a Box2D world.
110  */
111 class Box2DWorld : public QObject, public QQmlParserStatus, b2DestructionListener
112 {
113     Q_OBJECT
114 
115     Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
116     Q_PROPERTY(float timeStep READ timeStep WRITE setTimeStep NOTIFY timeStepChanged)
117     Q_PROPERTY(int velocityIterations READ velocityIterations WRITE setVelocityIterations NOTIFY velocityIterationsChanged)
118     Q_PROPERTY(int positionIterations READ positionIterations WRITE setPositionIterations NOTIFY positionIterationsChanged)
119     Q_PROPERTY(QPointF gravity READ gravity WRITE setGravity NOTIFY gravityChanged)
120     Q_PROPERTY(bool autoClearForces READ autoClearForces WRITE setAutoClearForces NOTIFY autoClearForcesChanged)
121     Q_PROPERTY(Box2DProfile *profile READ profile NOTIFY stepped)
122     Q_PROPERTY(float pixelsPerMeter READ pixelsPerMeter WRITE setPixelsPerMeter NOTIFY pixelsPerMeterChanged)
123     Q_PROPERTY(bool enableContactEvents READ enableContactEvents WRITE setEnableContactEvents NOTIFY enableContactEventsChanged)
124 
125     Q_INTERFACES(QQmlParserStatus)
126 
127 public:
128     explicit Box2DWorld(QObject *parent = 0);
129     ~Box2DWorld();
130 
131     float timeStep() const;
132     void setTimeStep(float timeStep);
133 
134     bool isRunning() const;
135     void setRunning(bool running);
136 
137     int velocityIterations() const;
138     void setVelocityIterations(int iterations);
139 
140     int positionIterations() const;
141     void setPositionIterations(int iterations);
142 
143     QPointF gravity() const;
144     void setGravity(const QPointF &gravity);
145 
146     bool autoClearForces() const;
147     void setAutoClearForces(bool autoClearForces);
148 
149     Box2DProfile *profile() const;
150 
151     bool enableContactEvents() const;
152     void setEnableContactEvents(bool enableContactEvents);
153 
154     float pixelsPerMeter() const;
155     void setPixelsPerMeter(float pixelsPerMeter);
156 
157     float metersPerPixel() const;
158     float pixelsPerMeterY() const;
159     float metersPerPixelY() const;
160 
161     float toPixels(float length) const;
162     float toMeters(float length) const;
163 
164     QPointF toPixels(const b2Vec2 &vec) const;
165     b2Vec2 toMeters(const QPointF &point) const;
166 
167     bool isSynchronizing() const;
168 
169     void classBegin();
170     void componentComplete();
171 
172     b2World &world();
173 
174     // b2DestructionListener interface
175     void SayGoodbye(b2Joint *joint);
176     void SayGoodbye(b2Fixture *fixture);
177 
178     Q_INVOKABLE void step();
179     Q_INVOKABLE void clearForces();
180     Q_INVOKABLE void rayCast(Box2DRayCast *rayCast,
181                              const QPointF &point1,
182                              const QPointF &point2);
183     static Box2DWorld * defaultWorld();
184 
185 signals:
186     void preSolve(Box2DContact * contact);
187     void postSolve(Box2DContact * contact);
188 
189     void timeStepChanged();
190     void velocityIterationsChanged();
191     void positionIterationsChanged();
192     void gravityChanged();
193     void autoClearForcesChanged();
194     void runningChanged();
195     void stepped();
196     void enableContactEventsChanged();
197     void pixelsPerMeterChanged();
198 
199 protected:
200     void enableContactListener(bool enable);
201 
202 private:
203     b2World mWorld;
204     ContactListener *mContactListener;
205     float mTimeStep;
206     int mVelocityIterations;
207     int mPositionIterations;
208     bool mComponentComplete;
209     bool mIsRunning;
210     bool mSynchronizing;
211     StepDriver *mStepDriver;
212     Box2DProfile *mProfile;
213     bool mEnableContactEvents;
214     float mPixelsPerMeter;
215 };
216 
217 
step()218 inline float Box2DProfile::step() const
219 {
220     return mWorld->GetProfile().step;
221 }
222 
collide()223 inline float Box2DProfile::collide() const
224 {
225     return mWorld->GetProfile().collide;
226 }
227 
solve()228 inline float Box2DProfile::solve() const
229 {
230     return mWorld->GetProfile().solve;
231 }
232 
solveInit()233 inline float Box2DProfile::solveInit() const
234 {
235     return mWorld->GetProfile().solveInit;
236 }
237 
solveVelocity()238 inline float Box2DProfile::solveVelocity() const
239 {
240     return mWorld->GetProfile().solveVelocity;
241 }
242 
solvePosition()243 inline float Box2DProfile::solvePosition() const
244 {
245     return mWorld->GetProfile().solvePosition;
246 }
247 
broadphase()248 inline float Box2DProfile::broadphase() const
249 {
250     return mWorld->GetProfile().broadphase;
251 }
252 
solveTOI()253 inline float Box2DProfile::solveTOI() const
254 {
255     return mWorld->GetProfile().solveTOI;
256 }
257 
synchronize()258 inline float Box2DProfile::synchronize() const
259 {
260     return mSynchronize;
261 }
262 
emitSignals()263 inline float Box2DProfile::emitSignals() const
264 {
265     return mEmitSignals;
266 }
267 
268 
269 /**
270  * The amount of time to step through each frame in seconds.
271  * By default it is 1 / 60.
272  */
timeStep()273 inline float Box2DWorld::timeStep() const
274 {
275     return mTimeStep;
276 }
277 
isRunning()278 inline bool Box2DWorld::isRunning() const
279 {
280     return mIsRunning;
281 }
282 
283 /**
284  * The number of velocity iterations used to process one step.
285  * 8 by default.
286  */
velocityIterations()287 inline int Box2DWorld::velocityIterations() const
288 {
289     return mVelocityIterations;
290 }
291 
292 /**
293  * The number of position iterations used to process one step.
294  * 3 by default.
295  */
positionIterations()296 inline int Box2DWorld::positionIterations() const
297 {
298     return mPositionIterations;
299 }
300 
autoClearForces()301 inline bool Box2DWorld::autoClearForces() const
302 {
303     return mWorld.GetAutoClearForces();
304 }
305 
profile()306 inline Box2DProfile *Box2DWorld::profile() const
307 {
308     return mProfile;
309 }
310 
enableContactEvents()311 inline bool Box2DWorld::enableContactEvents() const
312 {
313     return mEnableContactEvents;
314 }
315 
pixelsPerMeter()316 inline float Box2DWorld::pixelsPerMeter() const
317 {
318     return mPixelsPerMeter;
319 }
320 
metersPerPixel()321 inline float Box2DWorld::metersPerPixel() const
322 {
323     return 1.0f / pixelsPerMeter();
324 }
325 
pixelsPerMeterY()326 inline float Box2DWorld::pixelsPerMeterY() const
327 {
328     return -pixelsPerMeter(); // Y-axis inverted
329 }
330 
metersPerPixelY()331 inline float Box2DWorld::metersPerPixelY() const
332 {
333     return -metersPerPixel();
334 }
335 
336 /**
337  * Converts lengths from Box2D to QML units.
338  */
toPixels(float length)339 inline float Box2DWorld::toPixels(float length) const
340 {
341     return length * pixelsPerMeter();
342 }
343 
344 /**
345  * Converts lengths from QML to Box2D units.
346  */
toMeters(float length)347 inline float Box2DWorld::toMeters(float length) const
348 {
349     return length * metersPerPixel();
350 }
351 
352 /**
353  * Converts positions and sizes from Box2D to QML coordinates.
354  */
toPixels(const b2Vec2 & vec)355 inline QPointF Box2DWorld::toPixels(const b2Vec2 &vec) const
356 {
357     return QPointF(vec.x * pixelsPerMeter(),
358                    vec.y * pixelsPerMeterY());
359 }
360 
361 /**
362  * Converts positions and sizes from QML to Box2D coordinates.
363  */
toMeters(const QPointF & point)364 inline b2Vec2 Box2DWorld::toMeters(const QPointF &point) const
365 {
366     return b2Vec2(point.x() * metersPerPixel(),
367                   point.y() * metersPerPixelY());
368 }
369 
isSynchronizing()370 inline bool Box2DWorld::isSynchronizing() const
371 {
372     return mSynchronizing;
373 }
374 
world()375 inline b2World &Box2DWorld::world()
376 {
377     return mWorld;
378 }
379 
clearForces()380 inline void Box2DWorld::clearForces()
381 {
382     mWorld.ClearForces();
383 }
384 
385 
386 /**
387  * Inverts the y-axis as required for forces and velocities.
388  */
invertY(const b2Vec2 & vec)389 inline QPointF invertY(const b2Vec2 &vec)
390 {
391     return QPointF(vec.x, -vec.y);
392 }
393 
394 /**
395  * Inverts the y-axis as required for forces and velocities.
396  */
invertY(const QPointF & vec)397 inline b2Vec2 invertY(const QPointF &vec)
398 {
399     return b2Vec2(vec.x(), -vec.y());
400 }
401 
402 /**
403  * Converts angles from Box2D to QML values.
404  */
toDegrees(float radians)405 inline float toDegrees(float radians)
406 {
407     return -radians * 180 / b2_pi;
408 }
409 
410 /**
411  * Converts angles from QML to Box2D values.
412  */
toRadians(float degrees)413 inline float toRadians(float degrees)
414 {
415     return -degrees * b2_pi / 180;
416 }
417 
418 
419 QML_DECLARE_TYPE(Box2DWorld)
420 
421 #endif // BOX2DWORLD_H
422