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