1 /*
2  * box2dfixture.cpp
3  * Copyright (c) 2010-2011 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4  * Copyright (c) 2011 Daker Fernandes Pinheiro <daker.pinheiro@openbossa.org>
5  * Copyright (c) 2011 Alessandro Portale <alessandro.portale@nokia.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 #include "box2dfixture.h"
29 
30 #include "box2dbody.h"
31 #include "box2dworld.h"
32 
33 #include <QDebug>
34 
35 #include "Common/b2Math.h"
36 
Box2DFixture(QObject * parent)37 Box2DFixture::Box2DFixture(QObject *parent) :
38     QObject(parent),
39     mFixture(0),
40     mBody(0)
41 {
42     mFixtureDef.userData = this;
43 }
44 
density() const45 float Box2DFixture::density() const
46 {
47     return mFixtureDef.density;
48 }
49 
setDensity(float density)50 void Box2DFixture::setDensity(float density)
51 {
52     if (mFixtureDef.density == density)
53         return;
54 
55     mFixtureDef.density = density;
56     if (mFixture)
57         mFixture->SetDensity(density);
58     emit densityChanged();
59 }
60 
friction() const61 float Box2DFixture::friction() const
62 {
63     return mFixtureDef.friction;
64 }
65 
setFriction(float friction)66 void Box2DFixture::setFriction(float friction)
67 {
68     if (mFixtureDef.friction == friction)
69         return;
70 
71     mFixtureDef.friction = friction;
72     if (mFixture)
73         mFixture->SetFriction(friction);
74     emit frictionChanged();
75 }
76 
restitution() const77 float Box2DFixture::restitution() const
78 {
79     return mFixtureDef.restitution;
80 }
81 
setRestitution(float restitution)82 void Box2DFixture::setRestitution(float restitution)
83 {
84     if (mFixtureDef.restitution == restitution)
85         return;
86 
87     mFixtureDef.restitution = restitution;
88     if (mFixture)
89         mFixture->SetRestitution(restitution);
90     emit restitutionChanged();
91 }
92 
isSensor() const93 bool Box2DFixture::isSensor() const
94 {
95     return mFixtureDef.isSensor;
96 }
97 
setSensor(bool sensor)98 void Box2DFixture::setSensor(bool sensor)
99 {
100     if (mFixtureDef.isSensor == sensor)
101         return;
102 
103     mFixtureDef.isSensor = sensor;
104     if (mFixture)
105         mFixture->SetSensor(sensor);
106     emit sensorChanged();
107 }
108 
categories() const109 Box2DFixture::CategoryFlags Box2DFixture::categories() const
110 {
111     return CategoryFlags(mFixtureDef.filter.categoryBits);
112 }
113 
setCategories(CategoryFlags layers)114 void Box2DFixture::setCategories(CategoryFlags layers)
115 {
116     if (mFixtureDef.filter.categoryBits == layers)
117         return;
118 
119     mFixtureDef.filter.categoryBits = layers;
120     if (mFixture)
121         mFixture->SetFilterData(mFixtureDef.filter);
122 
123     emit categoriesChanged();
124 }
125 
collidesWith() const126 Box2DFixture::CategoryFlags Box2DFixture::collidesWith() const
127 {
128     return CategoryFlags(mFixtureDef.filter.maskBits);
129 }
130 
setCollidesWith(CategoryFlags layers)131 void Box2DFixture::setCollidesWith(CategoryFlags layers)
132 {
133     if (mFixtureDef.filter.maskBits == layers)
134         return;
135 
136     mFixtureDef.filter.maskBits = layers;
137     if (mFixture)
138         mFixture->SetFilterData(mFixtureDef.filter);
139 
140     emit collidesWithChanged();
141 }
142 
groupIndex() const143 int Box2DFixture::groupIndex() const
144 {
145     return mFixtureDef.filter.groupIndex;
146 }
147 
setGroupIndex(int groupIndex)148 void Box2DFixture::setGroupIndex(int groupIndex)
149 {
150     if (mFixtureDef.filter.groupIndex == groupIndex)
151         return;
152 
153     mFixtureDef.filter.groupIndex = groupIndex;
154     if (mFixture)
155         mFixture->SetFilterData(mFixtureDef.filter);
156 
157     emit groupIndexChanged();
158 }
159 
initialize(Box2DBody * body)160 void Box2DFixture::initialize(Box2DBody *body)
161 {
162     mBody = body;
163     b2Shape *shape = createShape();
164     if (!shape)
165         return;
166 
167     mFixtureDef.shape = shape;
168     mFixture = body->body()->CreateFixture(&mFixtureDef);
169     delete shape;
170 }
171 
recreateFixture()172 void Box2DFixture::recreateFixture()
173 {
174     if (!mBody)
175         return;
176     if (mFixture)
177         mBody->body()->DestroyFixture(mFixture);
178     initialize(mBody);
179 }
180 
getBody() const181 Box2DBody *Box2DFixture::getBody() const
182 {
183     return mBody;
184 }
185 
186 //=================== BOX =======================
187 
setX(qreal x)188 void Box2DBox::setX(qreal x)
189 {
190     if (mPosition.x() == x)
191         return;
192     mPosition.setX(x);
193     recreateFixture();
194     emit xChanged();
195 }
196 
setY(qreal y)197 void Box2DBox::setY(qreal y)
198 {
199     if (mPosition.y() == y)
200         return;
201     mPosition.setY(y);
202     recreateFixture();
203     emit yChanged();
204 }
205 
setWidth(qreal width)206 void Box2DBox::setWidth(qreal width)
207 {
208     if (mSize.width() == width)
209         return;
210     mSize.setWidth(width);
211     recreateFixture();
212     emit widthChanged();
213 }
214 
setHeight(qreal height)215 void Box2DBox::setHeight(qreal height)
216 {
217     if (mSize.height() == height)
218         return;
219     mSize.setHeight(height);
220     recreateFixture();
221     emit heightChanged();
222 }
223 
setRotation(qreal rotation)224 void Box2DBox::setRotation(qreal rotation)
225 {
226     if (mRotation == rotation)
227         return;
228     mRotation = rotation;
229     recreateFixture();
230     emit rotationChanged();
231 }
232 
createShape()233 b2Shape *Box2DBox::createShape()
234 {
235     const qreal halfWidth = width() * 0.5;
236     const qreal halfHeight = height() * 0.5;
237     const QPointF center(x() + halfWidth,
238                          y() + halfHeight);
239 
240     b2PolygonShape *shape = new b2PolygonShape;
241     shape->SetAsBox(b2Max(mBody->world()->toMeters(halfWidth), b2_linearSlop),
242                     b2Max(mBody->world()->toMeters(halfHeight), b2_linearSlop),
243                     mBody->world()->toMeters(center),
244                     toRadians(rotation()));
245 
246     return shape;
247 }
248 
249 //=================== CIRCLE =======================
250 
setX(qreal x)251 void Box2DCircle::setX(qreal x)
252 {
253     if (mPosition.x() == x)
254         return;
255     mPosition.setX(x);
256     recreateFixture();
257     emit xChanged();
258 }
259 
setY(qreal y)260 void Box2DCircle::setY(qreal y)
261 {
262     if (mPosition.y() == y)
263         return;
264     mPosition.setY(y);
265     recreateFixture();
266     emit yChanged();
267 }
268 
setRadius(float radius)269 void Box2DCircle::setRadius(float radius)
270 {
271     if (mRadius == radius)
272         return;
273     mRadius = radius;
274     recreateFixture();
275     emit radiusChanged();
276 }
277 
createShape()278 b2Shape *Box2DCircle::createShape()
279 {
280     b2CircleShape *shape = new b2CircleShape;
281 
282     shape->m_radius = mBody->world()->toMeters(radius());
283     shape->m_p = mBody->world()->toMeters(position() + QPointF(radius(), radius()));
284 
285     return shape;
286 }
287 
288 //=================== POLYGON =======================
289 
setVertices(const QVariantList & vertices)290 void Box2DPolygon::setVertices(const QVariantList &vertices)
291 {
292     if (vertices == mVertices)
293         return;
294 
295     mVertices = vertices;
296     recreateFixture();
297     emit verticesChanged();
298 }
299 
createShape()300 b2Shape *Box2DPolygon::createShape()
301 {
302     const int count = mVertices.length();
303     if (count < 2 || count > b2_maxPolygonVertices) {
304         qWarning() << "Polygon: Invalid number of vertices:" << count;
305         return 0;
306     }
307 
308     QScopedArrayPointer<b2Vec2> vertices(new b2Vec2[count]);
309 
310     for (int i = 0; i < count; ++i) {
311         vertices[i] = mBody->world()->toMeters(mVertices.at(i).toPointF());
312 
313         if (i > 0) {
314             if (b2DistanceSquared(vertices[i - 1], vertices[i]) <= b2_linearSlop * b2_linearSlop) {
315                 qWarning() << "Polygon: vertices are too close together";
316                 return 0;
317             }
318         }
319     }
320 
321     b2PolygonShape *shape = new b2PolygonShape;
322     shape->Set(vertices.data(), count);
323 
324     return shape;
325 }
326 
327 //=================== CHAIN =======================
328 
Box2DChain(QQuickItem * parent)329 Box2DChain::Box2DChain(QQuickItem *parent) :
330     Box2DFixture(parent),
331     mLoop(false),
332     mPrevVertexFlag(false),
333     mNextVertexFlag(false)
334 {
335 }
336 
setVertices(const QVariantList & vertices)337 void Box2DChain::setVertices(const QVariantList &vertices)
338 {
339     if (vertices == mVertices)
340         return;
341 
342     mVertices = vertices;
343     recreateFixture();
344     emit verticesChanged();
345 }
346 
setLoop(bool loop)347 void Box2DChain::setLoop(bool loop)
348 {
349     if (mLoop == loop)
350         return;
351 
352     mLoop = loop;
353     recreateFixture();
354     emit loopChanged();
355 }
356 
setPrevVertex(const QPointF & prevVertex)357 void Box2DChain::setPrevVertex(const QPointF &prevVertex)
358 {
359     if (mPrevVertexFlag && mPrevVertex == prevVertex)
360         return;
361 
362     mPrevVertex = prevVertex;
363     mPrevVertexFlag = true;
364     recreateFixture();
365     emit prevVertexChanged();
366 }
367 
setNextVertex(const QPointF & nextVertex)368 void Box2DChain::setNextVertex(const QPointF &nextVertex)
369 {
370     if (mNextVertexFlag && mNextVertex == nextVertex)
371         return;
372 
373     mNextVertex = nextVertex;
374     mNextVertexFlag = true;
375     recreateFixture();
376     emit nextVertexChanged();
377 }
378 
createShape()379 b2Shape *Box2DChain::createShape()
380 {
381     const int count = mVertices.length();
382 
383     if (count < 2 || (mLoop && count < 3)) {
384         qWarning() << "Chain: Invalid number of vertices:" << count;
385         return 0;
386     }
387 
388     QScopedArrayPointer<b2Vec2> vertices(new b2Vec2[count]);
389 
390     for (int i = 0; i < count; ++i) {
391         vertices[i] = mBody->world()->toMeters(mVertices.at(i).toPointF());
392 
393         if (i > 0) {
394             if (b2DistanceSquared(vertices[i - 1], vertices[i]) <= b2_linearSlop * b2_linearSlop) {
395                 qWarning() << "Chain: vertices are too close together";
396                 return 0;
397             }
398         }
399     }
400 
401     b2ChainShape *shape = new b2ChainShape;
402     if (mLoop) {
403         shape->CreateLoop(vertices.data(), count);
404     } else {
405         shape->CreateChain(vertices.data(), count);
406 
407         if (mPrevVertexFlag)
408             shape->SetPrevVertex(mBody->world()->toMeters(mPrevVertex));
409         if (mNextVertexFlag)
410             shape->SetNextVertex(mBody->world()->toMeters(mNextVertex));
411     }
412 
413     return shape;
414 }
415 
416 //=================== EDGE =======================
417 
setVertices(const QVariantList & vertices)418 void Box2DEdge::setVertices(const QVariantList &vertices)
419 {
420     if (vertices == mVertices)
421         return;
422 
423     mVertices = vertices;
424     recreateFixture();
425     emit verticesChanged();
426 }
427 
createShape()428 b2Shape *Box2DEdge::createShape()
429 {
430     const int count = mVertices.length();
431     if (count != 2) {
432         qWarning() << "Edge: Invalid number of vertices:" << count;
433         return 0;
434     }
435     const b2Vec2 vertex1 = mBody->world()->toMeters(mVertices.at(0).toPointF());
436     const b2Vec2 vertex2 = mBody->world()->toMeters(mVertices.at(1).toPointF());
437     if (b2DistanceSquared(vertex1, vertex2) <= b2_linearSlop * b2_linearSlop) {
438         qWarning() << "Edge: vertices are too close together";
439         return 0;
440     }
441     b2EdgeShape *shape = new b2EdgeShape;
442     shape->Set(vertex1, vertex2);
443 
444     return shape;
445 }
446