• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

louis-langholtz / PlayRho / 7311825320

24 Dec 2023 01:55AM UTC coverage: 99.301% (+0.009%) from 99.292%
7311825320

push

github

louis-langholtz
Attempts to test newly uncovvered paths of SetContact

11360 of 11440 relevant lines covered (99.3%)

16090965.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.81
/Library/source/playrho/d2/AabbTreeWorld.cpp
1
/*
2
 * Original work Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
3
 * Modified work Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho
4
 *
5
 * This software is provided 'as-is', without any express or implied
6
 * warranty. In no event will the authors be held liable for any damages
7
 * arising from the use of this software.
8
 *
9
 * Permission is granted to anyone to use this software for any purpose,
10
 * including commercial applications, and to alter it and redistribute it
11
 * freely, subject to the following restrictions:
12
 *
13
 * 1. The origin of this software must not be misrepresented; you must not
14
 *    claim that you wrote the original software. If you use this software
15
 *    in a product, an acknowledgment in the product documentation would be
16
 *    appreciated but is not required.
17
 * 2. Altered source versions must be plainly marked as such, and must not be
18
 *    misrepresented as being the original software.
19
 * 3. This notice may not be removed or altered from any source distribution.
20
 */
21

22
#include <algorithm>
23
#include <cassert> // for assert
24
#include <cstddef> // for std::size_t
25
#include <cstdint> // for std::uint32_t
26
#include <exception> // for std::throw_with_nested
27
#include <functional>
28
#include <iterator> // for std::next
29
#include <limits> // for std::numeric_limits
30
#include <map>
31
#include <optional>
32
#include <set>
33
#include <stdexcept> // for std::out_of_range
34
#include <tuple>
35
#include <utility> // for std::pair
36
#include <vector>
37

38
#ifdef DO_PAR_UNSEQ
39
#include <atomic>
40
#endif
41

42
//#define DO_THREADED
43
#if defined(DO_THREADED)
44
#include <future>
45
#endif
46

47
#include <playrho/BodyID.hpp>
48
#include <playrho/BodyType.hpp>
49
#include <playrho/Contact.hpp>
50
#include <playrho/Contactable.hpp>
51
#include <playrho/ContactID.hpp>
52
#include <playrho/ContactKey.hpp>
53
#include <playrho/ConstraintSolverConf.hpp>
54
#include <playrho/FlagGuard.hpp>
55
#include <playrho/InvalidArgument.hpp>
56
#include <playrho/Island.hpp>
57
#include <playrho/JointID.hpp>
58
#include <playrho/KeyedContactID.hpp>
59
#include <playrho/LengthError.hpp>
60
#include <playrho/Math.hpp>
61
#include <playrho/MovementConf.hpp>
62
#include <playrho/ObjectPool.hpp>
63
#include <playrho/OutOfRange.hpp>
64
#include <playrho/Real.hpp>
65
#include <playrho/Settings.hpp>
66
#include <playrho/ShapeID.hpp>
67
#include <playrho/Span.hpp>
68
#include <playrho/StepConf.hpp>
69
#include <playrho/StepStats.hpp>
70
#include <playrho/Templates.hpp>
71
#include <playrho/ToiConf.hpp>
72
#include <playrho/ToiOutput.hpp>
73
#include <playrho/to_underlying.hpp>
74
#include <playrho/UnitInterval.hpp>
75
#include <playrho/Units.hpp>
76
#include <playrho/Vector2.hpp>
77
#include <playrho/WrongState.hpp>
78
#include <playrho/ZeroToUnderOne.hpp>
79

80
#include <playrho/pmr/MemoryResource.hpp>
81
#include <playrho/pmr/PoolMemoryResource.hpp>
82

83
#include <playrho/d2/AABB.hpp>
84
#include <playrho/d2/AabbTreeWorld.hpp>
85
#include <playrho/d2/Body.hpp>
86
#include <playrho/d2/BodyConf.hpp>
87
#include <playrho/d2/BodyConstraint.hpp>
88
#include <playrho/d2/ContactImpulsesFunction.hpp>
89
#include <playrho/d2/ContactImpulsesList.hpp>
90
#include <playrho/d2/ContactSolver.hpp>
91
#include <playrho/d2/Distance.hpp>
92
#include <playrho/d2/DistanceConf.hpp>
93
#include <playrho/d2/DistanceJointConf.hpp>
94
#include <playrho/d2/DistanceProxy.hpp>
95
#include <playrho/d2/DynamicTree.hpp>
96
#include <playrho/d2/FrictionJointConf.hpp>
97
#include <playrho/d2/GearJointConf.hpp>
98
#include <playrho/d2/Joint.hpp>
99
#include <playrho/d2/Manifold.hpp>
100
#include <playrho/d2/Math.hpp>
101
#include <playrho/d2/MotorJointConf.hpp>
102
#include <playrho/d2/Position.hpp>
103
#include <playrho/d2/PositionConstraint.hpp>
104
#include <playrho/d2/PrismaticJointConf.hpp>
105
#include <playrho/d2/PulleyJointConf.hpp>
106
#include <playrho/d2/RayCastOutput.hpp>
107
#include <playrho/d2/RevoluteJointConf.hpp>
108
#include <playrho/d2/RopeJointConf.hpp>
109
#include <playrho/d2/Shape.hpp>
110
#include <playrho/d2/TargetJointConf.hpp>
111
#include <playrho/d2/TimeOfImpact.hpp>
112
#include <playrho/d2/Transformation.hpp>
113
#include <playrho/d2/Velocity.hpp>
114
#include <playrho/d2/VelocityConstraint.hpp>
115
#include <playrho/d2/WeldJointConf.hpp>
116
#include <playrho/d2/WheelJointConf.hpp>
117
#include <playrho/d2/WorldConf.hpp>
118
#include <playrho/d2/WorldManifold.hpp>
119

120
// Enable this macro to enable sorting ID lists like m_contacts. This results in more linearly
121
// accessed memory. Benchmarking hasn't found a significant performance improvement however but
122
// it does seem to decrease performance in smaller simulations.
123
#define DO_SORT_ID_LISTS 0
124

125
using std::for_each;
126
using std::remove;
127
using std::sort;
128
using std::transform;
129
using std::unique;
130

131
namespace playrho::d2 {
132

133
using playrho::size;
134

135
/// @brief Collection of body constraints.
136
using BodyConstraints = std::vector<BodyConstraint, pmr::polymorphic_allocator<BodyConstraint>>;
137

138
/// @brief Collection of position constraints.
139
using PositionConstraints = std::vector<PositionConstraint, pmr::polymorphic_allocator<PositionConstraint>>;
140

141
/// @brief Collection of velocity constraints.
142
using VelocityConstraints = std::vector<VelocityConstraint, pmr::polymorphic_allocator<VelocityConstraint>>;
143

144
/// @brief The contact updating configuration.
145
struct AabbTreeWorld::ContactUpdateConf
146
{
147
    DistanceConf distance; ///< Distance configuration data.
148
    Manifold::Conf manifold; ///< Manifold configuration data.
149
};
150

151
namespace {
152

153
constexpr auto idIsDestroyedMsg = "ID is destroyed";
154
constexpr auto worldIsLockedMsg = "world is locked";
155
constexpr auto noSuchBodyMsg = "no such body";
156
constexpr auto noSuchContactMsg = "no such contact";
157
constexpr auto noSuchManifoldMsg = "no such manifold";
158
constexpr auto noSuchShapeMsg = "no such shape";
159
constexpr auto noSuchJointMsg = "no such joint";
160

161
template <class Container, class U, class V, class Message>
162
auto At(Container &&container, ::playrho::detail::IndexingNamedType<U, V> id, Message &&msg)
108,568,573✔
163
    -> decltype(OutOfRange{id, std::forward<Message>(msg)}, // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
164
                std::forward<Container>(container).at(to_underlying(id)))
165
{
166
    try {
167
        return std::forward<Container>(container).at(to_underlying(id));
108,568,573✔
168
    }
169
    catch (const std::out_of_range&) {
112✔
170
        std::throw_with_nested(OutOfRange{id, std::forward<Message>(msg)}); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
112✔
171
    }
172
}
173

174
inline void IntegratePositions(const Span<const BodyID>& bodies,
502,994✔
175
                               const Span<BodyConstraint>& constraints,
176
                               Time h)
177
{
178
    assert(IsValid(h));
502,994✔
179
    for_each(cbegin(bodies), cend(bodies), [&](const auto& id) {
502,994✔
180
        auto& bc = constraints[to_underlying(id)];
3,114,501✔
181
        const auto velocity = bc.GetVelocity();
3,114,501✔
182
        const auto translation = h * velocity.linear;
3,114,501✔
183
        const auto rotation = h * velocity.angular;
3,114,501✔
184
        bc.SetPosition(bc.GetPosition() + Position{translation, rotation});
3,114,501✔
185
    });
3,114,501✔
186
}
502,994✔
187

188
/// Reports the given constraints to the listener.
189
/// @details
190
/// This calls the listener's PostSolve function for all size(contacts) elements of
191
/// the given array of constraints.
192
/// @param listener Listener to call.
193
/// @param constraints Array of m_contactCount contact velocity constraint elements.
194
inline void Report(const ContactImpulsesFunction& listener,
213,265✔
195
                   const Span<const ContactID>& contacts,
196
                   const Span<const VelocityConstraint>& constraints,
197
                   StepConf::iteration_type solved)
198
{
199
    const auto numContacts = size(contacts);
213,265✔
200
    for (auto i = decltype(numContacts){0}; i < numContacts; ++i)
424,671✔
201
    {
202
        listener(contacts[i], GetContactImpulses(constraints[i]), solved);
211,406✔
203
    }
204
}
213,265✔
205

206
inline void AssignImpulses(Manifold& var, const VelocityConstraint& vc)
4,959,967✔
207
{
208
    assert(var.GetPointCount() >= vc.GetPointCount());
4,959,967✔
209
    
210
    auto assignProc = [&](VelocityConstraint::size_type i) {
9,777,670✔
211
        const auto& point = vc.GetPointAt(i);
9,777,670✔
212
        var.SetPointImpulses(i, point.normalImpulse, point.tangentImpulse);
9,777,670✔
213
    };
14,737,637✔
214
#if 0
215
    // Branch free assignment causes problems in TilesComeToRest test.
216
    assignProc(1);
217
    assignProc(0);
218
#else
219
    const auto count = vc.GetPointCount();
4,959,967✔
220
    for (auto i = decltype(count){0}; i < count; ++i)
14,737,637✔
221
    {
222
        assignProc(i);
9,777,670✔
223
    }
224
#endif
225
}
4,959,967✔
226

227
/// @brief Calculates the "warm start" velocity deltas for the given velocity constraint.
228
VelocityPair CalcWarmStartVelocityDeltas(const VelocityConstraint& vc,
4,959,967✔
229
                                         const Span<const BodyConstraint>& bodies)
230
{
231
    auto vp = VelocityPair{Velocity{LinearVelocity2{}, 0_rpm}, Velocity{LinearVelocity2{}, 0_rpm}};
4,959,967✔
232

233
    const auto normal = vc.GetNormal();
4,959,967✔
234
    const auto tangent = vc.GetTangent();
4,959,967✔
235
    const auto pointCount = vc.GetPointCount();
4,959,967✔
236
    const auto& bodyA = bodies[to_underlying(vc.GetBodyA())];
4,959,967✔
237
    const auto& bodyB = bodies[to_underlying(vc.GetBodyB())];
4,959,967✔
238

239
    const auto invMassA = bodyA.GetInvMass();
4,959,967✔
240
    const auto invRotInertiaA = bodyA.GetInvRotInertia();
4,959,967✔
241

242
    const auto invMassB = bodyB.GetInvMass();
4,959,967✔
243
    const auto invRotInertiaB = bodyB.GetInvRotInertia();
4,959,967✔
244

245
    for (auto j = decltype(pointCount){0}; j < pointCount; ++j) {
14,737,637✔
246
        // inverse moment of inertia : L^-2 M^-1 QP^2
247
        // P is M L T^-2
248
        // GetPointRelPosA() is Length2
249
        // Cross(Length2, P) is: M L^2 T^-2
250
        // L^-2 M^-1 QP^2 M L^2 T^-2 is: QP^2 T^-2
251
        const auto& vcp = vc.GetPointAt(j);
9,777,670✔
252
        const auto P = vcp.normalImpulse * normal + vcp.tangentImpulse * tangent;
9,777,670✔
253
        const auto LA = Cross(vcp.relA, P) / Radian;
9,777,670✔
254
        const auto LB = Cross(vcp.relB, P) / Radian;
9,777,670✔
255
        std::get<0>(vp) -= Velocity{invMassA * P, invRotInertiaA * LA};
9,777,670✔
256
        std::get<1>(vp) += Velocity{invMassB * P, invRotInertiaB * LB};
9,777,670✔
257
    }
258

259
    return vp;
4,959,967✔
260
}
261

262
void WarmStartVelocities(const Span<const VelocityConstraint>& velConstraints,
463,315✔
263
                         const Span<BodyConstraint>& bodies)
264
{
265
    for_each(cbegin(velConstraints), cend(velConstraints), [&](const VelocityConstraint& vc) {
463,315✔
266
        const auto vp = CalcWarmStartVelocityDeltas(vc, bodies);
4,959,967✔
267
        auto& bodyA = bodies[to_underlying(vc.GetBodyA())];
4,959,967✔
268
        auto& bodyB = bodies[to_underlying(vc.GetBodyB())];
4,959,967✔
269
        bodyA.SetVelocity(bodyA.GetVelocity() + std::get<0>(vp));
4,959,967✔
270
        bodyB.SetVelocity(bodyB.GetVelocity() + std::get<1>(vp));
4,959,967✔
271
    });
4,959,967✔
272
}
463,315✔
273

274
BodyConstraints GetBodyConstraints(pmr::memory_resource& resource,
502,994✔
275
                                   const Span<const BodyID>& bodies,
276
                                   const ObjectPool<Body>& bodyBuffer,
277
                                   Time h, const MovementConf& conf)
278
{
279
    auto constraints = BodyConstraints{&resource};
502,994✔
280
    constraints.resize(size(bodyBuffer)); // can't be size(bodies)!
502,994✔
281
    for (const auto& id: bodies) {
3,617,495✔
282
        constraints[to_underlying(id)] = GetBodyConstraint(bodyBuffer[to_underlying(id)], h, conf);
3,114,501✔
283
    }
284
    return constraints;
502,994✔
285
}
×
286

287
PositionConstraints GetPositionConstraints(pmr::memory_resource& resource,
502,994✔
288
                                           const Span<const ContactID>& contacts,
289
                                           const ObjectPool<Contact>& contactBuffer,
290
                                           const ObjectPool<Manifold>& manifoldBuffer,
291
                                           const ObjectPool<Shape>& shapeBuffer)
292
{
293
    auto constraints = PositionConstraints{&resource};
502,994✔
294
    constraints.reserve(size(contacts));
502,994✔
295
    transform(cbegin(contacts), cend(contacts), back_inserter(constraints),
502,994✔
296
              [&](const ContactID& contactID) {
5,035,767✔
297
        const auto& contact = contactBuffer[to_underlying(contactID)];
5,035,767✔
298
        const auto shapeA = GetShapeA(contact);
5,035,767✔
299
        const auto shapeB = GetShapeB(contact);
5,035,767✔
300
        const auto indexA = GetChildIndexA(contact);
5,035,767✔
301
        const auto indexB = GetChildIndexB(contact);
5,035,767✔
302
        const auto bodyA = GetBodyA(contact);
5,035,767✔
303
        const auto bodyB = GetBodyB(contact);
5,035,767✔
304
        const auto radiusA = GetVertexRadius(shapeBuffer[to_underlying(shapeA)], indexA);
5,035,767✔
305
        const auto radiusB = GetVertexRadius(shapeBuffer[to_underlying(shapeB)], indexB);
5,035,767✔
306
        const auto& manifold = manifoldBuffer[to_underlying(contactID)];
5,035,767✔
307
        return PositionConstraint{manifold, bodyA, bodyB, radiusA + radiusB};
5,035,767✔
308
    });
309
    return constraints;
502,994✔
310
}
×
311

312
/// @brief Gets the velocity constraints for the given inputs.
313
/// @details Inializes the velocity constraints with the position dependent portions of
314
///   the current position constraints.
315
/// @post Velocity constraints will have their "normal" field set to the world manifold
316
///   normal for them.
317
/// @post Velocity constraints will have their constraint points set.
318
/// @see SolveVelocityConstraints.
319
VelocityConstraints GetVelocityConstraints(pmr::memory_resource& resource,
502,994✔
320
                                           const Span<const ContactID>& contacts,
321
                                           const ObjectPool<Contact>& contactBuffer,
322
                                           const ObjectPool<Manifold>& manifoldBuffer,
323
                                           const ObjectPool<Shape>& shapeBuffer,
324
                                           const Span<const BodyConstraint>& bodies,
325
                                           const VelocityConstraint::Conf conf)
326
{
327
    auto velConstraints = VelocityConstraints{&resource};
502,994✔
328
    velConstraints.reserve(size(contacts));
502,994✔
329
    transform(cbegin(contacts), cend(contacts), back_inserter(velConstraints),
502,994✔
330
              [&](const auto& contactID) {
5,035,767✔
331
        const auto& contact = contactBuffer[to_underlying(contactID)];
5,035,767✔
332
        const auto bodyA = GetBodyA(contact);
5,035,767✔
333
        const auto bodyB = GetBodyB(contact);
5,035,767✔
334
        const auto shapeIdA = GetShapeA(contact);
5,035,767✔
335
        const auto shapeIdB = GetShapeB(contact);
5,035,767✔
336
        const auto indexA = GetChildIndexA(contact);
5,035,767✔
337
        const auto indexB = GetChildIndexB(contact);
5,035,767✔
338
        const auto friction = GetFriction(contact);
5,035,767✔
339
        const auto restitution = GetRestitution(contact);
5,035,767✔
340
        const auto tangentSpeed = GetTangentSpeed(contact);
5,035,767✔
341
        const auto& bodyConstraintA = bodies[to_underlying(bodyA)];
5,035,767✔
342
        const auto& bodyConstraintB = bodies[to_underlying(bodyB)];
5,035,767✔
343
        const auto radiusA = GetVertexRadius(shapeBuffer[to_underlying(shapeIdA)], indexA);
5,035,767✔
344
        const auto radiusB = GetVertexRadius(shapeBuffer[to_underlying(shapeIdB)], indexB);
5,035,767✔
345
        const auto xfA = GetTransformation(bodyConstraintA.GetPosition(),
5,035,767✔
346
                                           bodyConstraintA.GetLocalCenter());
5,035,767✔
347
        const auto xfB = GetTransformation(bodyConstraintB.GetPosition(),
5,035,767✔
348
                                           bodyConstraintB.GetLocalCenter());
5,035,767✔
349
        const auto& manifold = manifoldBuffer[to_underlying(contactID)];
5,035,767✔
350
        return VelocityConstraint{friction, restitution, tangentSpeed,
351
            GetWorldManifold(manifold, xfA, radiusA, xfB, radiusB),
10,071,534✔
352
            bodyA, bodyB, bodies, conf};
15,107,301✔
353
    });
354
    return velConstraints;
502,994✔
355
}
×
356

357
/// "Solves" the velocity constraints.
358
/// @details Updates the velocities and velocity constraint points' normal and tangent impulses.
359
/// @pre <code>UpdateVelocityConstraints</code> has been called on the velocity constraints.
360
/// @return Maximum momentum used for solving both the tangential and normal portions of
361
///   the velocity constraints.
362
Momentum SolveVelocityConstraintsViaGS(const Span<VelocityConstraint>& velConstraints,
758,450✔
363
                                       const Span<BodyConstraint>& bodies)
364
{
365
    auto maxIncImpulse = 0_Ns;
758,450✔
366
    for_each(begin(velConstraints), end(velConstraints), [&](VelocityConstraint& vc) {
758,450✔
367
        maxIncImpulse = std::max(maxIncImpulse, GaussSeidel::SolveVelocityConstraint(vc, bodies));
38,669,341✔
368
    });
38,669,341✔
369
    return maxIncImpulse;
758,450✔
370
}
371

372
/// Solves the given position constraints.
373
/// @details This updates positions (and nothing else) by calling the position constraint solving function.
374
/// @note Can't expect the returned minimum separation to be greater than or equal to
375
///  <code>-conf.linearSlop</code> because code won't push the separation above this
376
///   amount to begin with.
377
/// @return Minimum separation.
378
Length SolvePositionConstraintsViaGS(const Span<const PositionConstraint>& posConstraints,
575,267✔
379
                                     const Span<BodyConstraint>& bodies,
380
                                     const ConstraintSolverConf& conf)
381
{
382
    auto minSeparation = std::numeric_limits<Length>::infinity();
575,267✔
383
    for_each(begin(posConstraints), end(posConstraints), [&](const PositionConstraint &pc) {
575,267✔
384
        assert(pc.bodyA != pc.bodyB); // Confirms ContactManager::Add() did its job.
14,561,715✔
385
        const auto res = GaussSeidel::SolvePositionConstraint(pc, true, true, bodies, conf);
14,561,715✔
386
        bodies[to_underlying(pc.bodyA)].SetPosition(res.pos_a);
14,561,715✔
387
        bodies[to_underlying(pc.bodyB)].SetPosition(res.pos_b);
14,561,715✔
388
        minSeparation = std::min(minSeparation, res.min_separation);
14,561,715✔
389
    });
14,561,715✔
390
    return minSeparation;
575,267✔
391
}
392

393
inline Time GetUnderActiveTime(const Body& b, const StepConf& conf) noexcept
3,027,493✔
394
{
395
    const auto underactive = IsUnderActive(GetVelocity(b), conf.linearSleepTolerance,
3,027,493✔
396
                                           conf.angularSleepTolerance);
3,027,493✔
397
    const auto sleepable = IsSleepingAllowed(b);
3,027,493✔
398
    return (sleepable && underactive)? GetUnderActiveTime(b) + conf.deltaTime: 0_s;
3,027,493✔
399
}
400

401
inline Time UpdateUnderActiveTimes(const Span<const BodyID>& bodies,
463,329✔
402
                                   ObjectPool<Body>& bodyBuffer,
403
                                   const StepConf& conf)
404
{
405
    auto minUnderActiveTime = std::numeric_limits<Time>::infinity();
463,329✔
406
    for_each(cbegin(bodies), cend(bodies), [&](const auto& bodyID) {
463,329✔
407
        auto& b = bodyBuffer[to_underlying(bodyID)];
3,035,171✔
408
        if (IsSpeedable(b)) {
3,035,171✔
409
            const auto underActiveTime = GetUnderActiveTime(b, conf);
3,027,493✔
410
            b.SetUnderActiveTime(underActiveTime);
3,027,493✔
411
            minUnderActiveTime = std::min(minUnderActiveTime, underActiveTime);
3,027,493✔
412
        }
413
    });
3,035,171✔
414
    return minUnderActiveTime;
463,329✔
415
}
416

417
inline BodyCounter Sleepem(const Span<const BodyID>& bodies,
38✔
418
                           ObjectPool<Body>& bodyBuffer)
419
{
420
    auto unawoken = BodyCounter{0};
38✔
421
    for_each(cbegin(bodies), cend(bodies), [&](const auto& bodyID) {
38✔
422
        if (Unawaken(bodyBuffer[to_underlying(bodyID)])) {
1,424✔
423
            ++unawoken;
1,406✔
424
        }
425
    });
1,424✔
426
    return unawoken;
38✔
427
}
428

429
inline bool IsValidForTime(ToiOutput::State state) noexcept
2,030,688✔
430
{
431
    return state == ToiOutput::e_touching;
2,030,688✔
432
}
433

434
bool FlagForFiltering(ObjectPool<Contact>& contactBuffer, BodyID bodyA,
547,346✔
435
                      const Span<const std::tuple<ContactKey, ContactID>>& contactsBodyB,
436
                      BodyID bodyB) noexcept
437
{
438
    auto anyFlagged = false;
547,346✔
439
    for (const auto& ci: contactsBodyB) {
2,627,330✔
440
        auto& contact = contactBuffer[to_underlying(std::get<ContactID>(ci))];
2,079,984✔
441
        if (GetOtherBody(contact, bodyB) == bodyA) {
2,079,984✔
442
            // Flag the contact for filtering at the next time step (where either
443
            // body is awake).
444
            contact.FlagForFiltering();
4✔
445
            anyFlagged = true;
4✔
446
        }
447
    }
448
    return anyFlagged;
547,346✔
449
}
450

451
/// @brief Gets the update configuration from the given step configuration data.
452
AabbTreeWorld::ContactUpdateConf GetUpdateConf(const StepConf& conf) noexcept
301,913✔
453
{
454
    return AabbTreeWorld::ContactUpdateConf{GetDistanceConf(conf), GetManifoldConf(conf)};
301,913✔
455
}
456

457
template <typename T>
458
void FlagForUpdating(ObjectPool<Contact>& contactsBuffer, const T& contacts) noexcept
2,731,783✔
459
{
460
    std::for_each(begin(contacts), end(contacts), [&](const auto& ci) {
2,731,783✔
461
        contactsBuffer[to_underlying(std::get<ContactID>(ci))].FlagForUpdating();
12,986,285✔
462
    });
463
}
2,731,783✔
464

465
inline bool EitherIsAccelerable(const Body& lhs, const Body& rhs) noexcept
113,614✔
466
{
467
    return IsAccelerable(lhs) || IsAccelerable(rhs);
113,614✔
468
}
469

470
bool ShouldCollide(const ObjectPool<Joint>& jointBuffer,
113,602✔
471
                   const ObjectPool<BodyJointIDs>& bodyJoints,
472
                   BodyID lhs, BodyID rhs)
473
{
474
    // Does a joint prevent collision?
475
    const auto& joints = bodyJoints[to_underlying(lhs)];
113,602✔
476
    const auto it = std::find_if(cbegin(joints), cend(joints), [&](const auto& ji) {
113,602✔
477
        return (std::get<BodyID>(ji) == rhs) &&
202✔
478
        !GetCollideConnected(jointBuffer[to_underlying(std::get<JointID>(ji))]);
202✔
479
    });
480
    return it == end(joints);
113,602✔
481
}
482

483
void Unset(std::vector<bool>& islanded, const Span<const BodyID>& elements)
222,553✔
484
{
485
    for (const auto& element: elements) {
4,303,230✔
486
        islanded[to_underlying(element)] = false;
4,080,677✔
487
    }
488
}
222,553✔
489

490
void Unset(std::vector<bool>& islanded, const Span<const std::pair<ContactKey, ContactID>>& elements)
222,553✔
491
{
492
    for (const auto& element: elements) {
6,979,044✔
493
        islanded[to_underlying(std::get<ContactID>(element))] = false;
6,756,491✔
494
    }
495
}
222,553✔
496

497
void Unset(std::vector<bool>& islanded, const Span<const std::tuple<ContactKey, ContactID>>& elements)
39,691✔
498
{
499
    for (const auto& element: elements) {
196,451✔
500
        islanded[to_underlying(std::get<ContactID>(element))] = false;
156,760✔
501
    }
502
}
39,691✔
503

504
/// @brief Reset bodies for solve TOI.
505
void ResetBodiesForSolveTOI(BodyIDs& bodies, ObjectPool<Body>& buffer) noexcept
222,553✔
506
{
507
    for_each(begin(bodies), end(bodies), [&](const auto& body) {
222,553✔
508
        buffer[to_underlying(body)].ResetAlpha0();
4,080,677✔
509
    });
4,080,677✔
510
}
222,553✔
511

512
/// @brief Reset contacts for solve TOI.
513
void ResetBodyContactsForSolveTOI(ObjectPool<Contact>& buffer,
39,691✔
514
                                  const Span<const std::tuple<ContactKey, ContactID>>& contacts) noexcept
515
{
516
    // Invalidate all contact TOIs on this displaced body.
517
    for_each(cbegin(contacts), cend(contacts), [&buffer](const auto& ci) {
39,691✔
518
        SetToi(buffer[to_underlying(std::get<ContactID>(ci))], {});
156,760✔
519
    });
156,760✔
520
}
39,691✔
521

522
/// @brief Reset contacts for solve TOI.
523
void ResetContactsForSolveTOI(ObjectPool<Contact>& buffer,
222,553✔
524
                              const KeyedContactIDs& contacts) noexcept
525
{
526
    for_each(begin(contacts), end(contacts), [&buffer](const auto& c) {
222,553✔
527
        auto& contact = buffer[to_underlying(std::get<ContactID>(c))];
6,756,491✔
528
        SetToi(contact, {});
6,756,491✔
529
        SetToiCount(contact, 0);
6,756,491✔
530
    });
6,756,491✔
531
}
222,553✔
532

533
/// @brief Destroys proxies of all tree nodes with the given body and shape identifiers.
534
void DestroyProxies(DynamicTree& tree, BodyID bodyId, ShapeID shapeId, ProxyIDs& proxies) noexcept
32✔
535
{
536
    const auto n = tree.GetNodeCapacity();
32✔
537
    for (auto i = DynamicTree::Size(0); i < n; ++i) {
131,104✔
538
        if (DynamicTree::IsLeaf(tree.GetHeight(i))) {
131,072✔
539
            const auto leaf = tree.GetLeafData(i);
4✔
540
            if ((leaf.bodyId == bodyId) && (leaf.shapeId == shapeId)) {
4✔
541
                EraseFirst(proxies, i);
×
542
                tree.DestroyLeaf(i);
×
543
            }
544
        }
545
    }
546
}
32✔
547

548
auto CreateProxies(DynamicTree& tree,
25,602✔
549
                   BodyID bodyID, ShapeID shapeID, const Shape& shape,
550
                   const Transformation& xfm, Length aabbExtension,
551
                   ProxyIDs& fixtureProxies,
552
                   ProxyIDs& otherProxies) -> ChildCounter
553
{
554
    // Reserve proxy space and create proxies in the broad-phase.
555
    const auto childCount = GetChildCount(shape);
25,602✔
556
    fixtureProxies.reserve(size(fixtureProxies) + childCount);
25,602✔
557
    otherProxies.reserve(size(otherProxies) + childCount);
25,602✔
558
    for (auto childIndex = decltype(childCount){0}; childIndex < childCount; ++childIndex) {
51,214✔
559
        const auto dp = GetChild(shape, childIndex);
25,612✔
560
        const auto aabb = playrho::d2::ComputeAABB(dp, xfm);
25,612✔
561
        const auto fattenedAABB = GetFattenedAABB(aabb, aabbExtension);
25,612✔
562
        const auto treeId = tree.CreateLeaf(fattenedAABB, Contactable{
25,612✔
563
            bodyID, shapeID, childIndex});
25,612✔
564
        fixtureProxies.push_back(treeId);
25,612✔
565
        otherProxies.push_back(treeId);
25,612✔
566
    }
567
    return childCount;
25,602✔
568
}
569

570
template <typename Element, typename Value>
571
auto FindTypeValue(const std::vector<Element>& container, const Value& value)
531,360✔
572
{
573
    const auto last = end(container);
531,360✔
574
    auto it = std::find_if(begin(container), last, [value](const auto& elem) {
1,167,421✔
575
        return std::get<Value>(elem) == value;
636,061✔
576
    });
577
    return (it != last)? std::optional<decltype(it)>{it}: std::optional<decltype(it)>{};
1,062,720✔
578
}
579

580
template <class C, class V>
581
auto Find(const C& c, const V& value) -> decltype(find(begin(c), end(c), value), bool{})
18✔
582
{
583
    const auto last = end(c);
18✔
584
    return find(begin(c), last, value) != last;
18✔
585
}
586

587
void Erase(BodyContactIDs& contacts, const std::function<bool(ContactID)>& callback)
90✔
588
{
589
    auto last = end(contacts);
90✔
590
    auto iter = begin(contacts);
90✔
591
    auto index = KeyedContactIDs::difference_type(0);
90✔
592
    while (iter != last) {
96✔
593
        const auto contact = std::get<ContactID>(*iter);
6✔
594
        if (callback(contact)) {
6✔
595
            contacts.erase(iter);
6✔
596
            iter = begin(contacts) + index;
6✔
597
            last = end(contacts);
6✔
598
        }
599
        else {
600
            iter = std::next(iter);
×
601
            ++index;
×
602
        }
603
    }
604
}
90✔
605

606
ProxyIDs FindProxies(const DynamicTree& tree, BodyID bodyId)
40✔
607
{
608
    ProxyIDs result;
40✔
609
    const auto n = tree.GetNodeCapacity();
40✔
610
    for (auto i = static_cast<decltype(tree.GetNodeCapacity())>(0); i < n; ++i) {
163,880✔
611
        if (DynamicTree::IsLeaf(tree.GetHeight(i))) {
163,840✔
612
            const auto leaf = tree.GetLeafData(i);
18✔
613
            if (leaf.bodyId == bodyId) {
18✔
614
                result.push_back(i);
12✔
615
            }
616
        }
617
    }
618
    return result;
40✔
619
}
×
620

621
template <class Function>
622
auto ForMatchingProxies(const DynamicTree& tree, ShapeID shapeId, Function f)
4✔
623
-> decltype(f(DynamicTree::Size{}), std::declval<void>())
624
{
625
    const auto n = tree.GetNodeCapacity();
4✔
626
    for (auto i = static_cast<decltype(tree.GetNodeCapacity())>(0); i < n; ++i) {
16,388✔
627
        if (DynamicTree::IsLeaf(tree.GetHeight(i))) {
16,384✔
628
            if (tree.GetLeafData(i).shapeId == shapeId) {
4✔
629
                f(i);
4✔
630
            }
631
        }
632
    }
633
}
4✔
634

635
std::pair<std::vector<ShapeID>, std::vector<ShapeID>>
636
GetOldAndNewShapeIDs(const Body& oldBody, const Body& newBody)
224,459✔
637
{
638
    if (IsEnabled(oldBody) && IsEnabled(newBody)) {
224,459✔
639
        auto oldShapeIds = std::vector<ShapeID>{};
224,431✔
640
        auto newShapeIds = std::vector<ShapeID>{};
224,431✔
641
        auto oldmap = std::map<ShapeID, int>{};
224,431✔
642
        auto newmap = std::map<ShapeID, int>{};
224,431✔
643
        for (auto&& i: oldBody.GetShapes()) {
75,255,634✔
644
            ++oldmap[i];
75,031,203✔
645
            --newmap[i];
75,031,203✔
646
        }
647
        for (auto&& i: newBody.GetShapes()) {
75,295,968✔
648
            --oldmap[i];
75,071,537✔
649
            ++newmap[i];
75,071,537✔
650
        }
651
        for (auto&& entry: oldmap) {
300,952✔
652
            for (auto i = 0; i < entry.second; ++i) {
76,543✔
653
                oldShapeIds.push_back(entry.first);
22✔
654
            }
655
        }
656
        for (auto&& entry: newmap) {
300,952✔
657
            for (auto i = 0; i < entry.second; ++i) {
116,877✔
658
                newShapeIds.push_back(entry.first);
40,356✔
659
            }
660
        }
661
        return {oldShapeIds, newShapeIds};
224,431✔
662
    }
224,431✔
663
    if (IsEnabled(newBody)) {
28✔
664
        return {std::vector<ShapeID>{}, newBody.GetShapes()};
20✔
665
    }
666
    if (IsEnabled(oldBody)) {
18✔
667
        return {oldBody.GetShapes(), std::vector<ShapeID>{}};
24✔
668
    }
669
    return {};
6✔
670
}
671

672
template <class T, class U>
673
void ResizeAndReset(std::vector<T>& vector, typename std::vector<T>::size_type newSize, const U& newValue)
667,659✔
674
{
675
    std::fill(begin(vector),
1,335,318✔
676
              begin(vector) + ToSigned(std::min(size(vector), newSize)),
1,335,318✔
677
              newValue);
678
    vector.resize(newSize);
667,659✔
679
}
667,659✔
680

681
/// @brief Removes <em>unspeedables</em> from the is <em>is-in-island</em> state.
682
BodyIDs::size_type
683
RemoveUnspeedablesFromIslanded(const Span<const BodyID>& bodies,
502,994✔
684
                               const ObjectPool<Body>& buffer,
685
                               std::vector<bool>& islanded)
686
{
687
    // Allow static bodies to participate in other islands.
688
    auto numRemoved = BodyIDs::size_type{0};
502,994✔
689
    for_each(begin(bodies), end(bodies), [&](BodyID id) {
502,994✔
690
        if (!IsSpeedable(buffer[to_underlying(id)])) {
3,114,501✔
691
            islanded[to_underlying(id)] = false;
47,317✔
692
            ++numRemoved;
47,317✔
693
        }
694
    });
3,114,501✔
695
    return numRemoved;
502,994✔
696
}
697

698
auto FindContacts(pmr::memory_resource& resource,
484,983✔
699
                  const DynamicTree& tree,
700
                  ProxyIDs&& proxies)
701
    -> std::vector<AabbTreeWorld::ProxyKey, pmr::polymorphic_allocator<AabbTreeWorld::ProxyKey>>
702
{
703
    std::vector<AabbTreeWorld::ProxyKey, pmr::polymorphic_allocator<AabbTreeWorld::ProxyKey>> proxyKeys{&resource};
484,983✔
704
    // Never need more than tree.GetLeafCount(), but in case big, use smaller default...
705
    static constexpr auto DefaultReserveSize = 256u;
706
    proxyKeys.reserve(std::min(tree.GetLeafCount(), DefaultReserveSize));
484,983✔
707

708
    // Accumalate contact keys for pairs of nodes that are overlapping and aren't identical.
709
    // Note that if the dynamic tree node provides the body index, it's assumed to be faster
710
    // to eliminate any node pairs that have the same body here before the key pairs are
711
    // sorted.
712
    for_each(cbegin(proxies), cend(proxies), [&](DynamicTree::Size pid) {
484,983✔
713
        const auto leaf0 = tree.GetLeafData(pid);
91,596✔
714
        const auto aabb = tree.GetAABB(pid);
91,596✔
715
        Query(tree, aabb, [pid,leaf0,&proxyKeys,&tree](DynamicTree::Size nodeId) {
91,596✔
716
            const auto leaf1 = tree.GetLeafData(nodeId);
257,732✔
717
            // A proxy cannot form a pair with itself.
718
            if ((nodeId != pid) && (leaf0.bodyId != leaf1.bodyId)) {
257,732✔
719
                const auto key = ContactKey{pid, nodeId};
136,618✔
720
                if (key.GetMin() == pid) {
136,618✔
721
                    proxyKeys.emplace_back(key, leaf0, leaf1);
62,824✔
722
                }
723
                else {
724
                    proxyKeys.emplace_back(key, leaf1, leaf0);
73,794✔
725
                }
726
            }
727
            return DynamicTreeOpcode::Continue;
257,732✔
728
        });
729
    });
91,596✔
730

731
    // Sort and eliminate any duplicate contact keys.
732
    sort(begin(proxyKeys), end(proxyKeys),
484,983✔
733
         [](const AabbTreeWorld::ProxyKey& a, const AabbTreeWorld::ProxyKey& b) {
1,047,546✔
734
        return std::get<0>(a) < std::get<0>(b);
1,047,546✔
735
    });
736
    proxyKeys.erase(unique(begin(proxyKeys), end(proxyKeys)), end(proxyKeys));
484,983✔
737
    return proxyKeys;
484,983✔
738
}
×
739

740
auto GetBodyStackOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
560✔
741
{
742
    return {conf.reserveBuffers, conf.reserveBodyStack * sizeof(BodyID)};
560✔
743
}
744

745
auto GetBodyConstraintOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
560✔
746
{
747
    return {conf.reserveBuffers, conf.reserveBodyConstraints * sizeof(BodyConstraint)};
560✔
748
}
749

750
auto GetPositionConstraintsOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
560✔
751
{
752
    return {conf.reserveBuffers, conf.reserveDistanceConstraints * sizeof(PositionConstraint)};
560✔
753
}
754

755
auto GetVelocityConstraintsOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
560✔
756
{
757
    return {conf.reserveBuffers, conf.reserveDistanceConstraints * sizeof(VelocityConstraint)};
560✔
758
}
759

760
auto GetProxyKeysOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
560✔
761
{
762
    return {conf.reserveBuffers, conf.reserveContactKeys * sizeof(AabbTreeWorld::ProxyKey)};
560✔
763
}
764

765
auto IsGeomChanged(const Shape& shape0, const Shape& shape1) -> bool
48✔
766
{
767
    const auto numKids0 = GetChildCount(shape0);
48✔
768
    const auto numKids1 = GetChildCount(shape1);
48✔
769
    if (numKids0 != numKids1) {
48✔
770
        return true;
2✔
771
    }
772
    for (auto child = 0u; child < numKids1; ++child) {
84✔
773
        const auto distanceProxy0 = GetChild(shape0, child);
46✔
774
        const auto distanceProxy1 = GetChild(shape1, child);
46✔
775
        if (distanceProxy0 != distanceProxy1) {
46✔
776
            return true;
8✔
777
        }
778
    }
779
    return false;
38✔
780
}
781

782
auto Append(std::vector<std::pair<BodyID, ShapeID>>& fixtures,
377,605✔
783
            BodyID bodyId, Span<const ShapeID> shapeIds) -> void
784
{
785
    for (const auto shapeId: shapeIds) {
423,323✔
786
        fixtures.emplace_back(bodyId, shapeId);
45,718✔
787
    }
788
}
377,605✔
789

790
template <class Container, class ElementType, class Message>
791
auto Validate(const Container& container, const Span<const ElementType>& ids, Message &&msg)
377,613✔
792
-> decltype(container.at(to_underlying(ElementType{})), std::declval<void>())
793
{
794
    for (const auto& id: ids) {
75,454,520✔
795
        At(container, id, std::forward<Message>(msg));
75,076,911✔
796
    }
797
}
377,609✔
798

799
auto SetAwake(ObjectPool<Body>& bodies, const Contact& c) -> void
6✔
800
{
801
    SetAwake(bodies[to_underlying(GetBodyA(c))]);
6✔
802
    SetAwake(bodies[to_underlying(GetBodyB(c))]);
6✔
803
}
6✔
804

805
} // anonymous namespace
806

807
AabbTreeWorld::AabbTreeWorld(const WorldConf& conf):
560✔
808
    m_statsResource(conf.doStats? conf.upstream: nullptr),
560✔
809
    m_bodyStackResource(GetBodyStackOpts(conf),
560✔
810
                        conf.doStats? &m_statsResource: conf.upstream),
560✔
811
    m_bodyConstraintsResource(GetBodyConstraintOpts(conf),
560✔
812
                              conf.doStats? &m_statsResource: conf.upstream),
560✔
813
    m_positionConstraintsResource(GetPositionConstraintsOpts(conf),
560✔
814
                                  conf.doStats? &m_statsResource: conf.upstream),
560✔
815
    m_velocityConstraintsResource(GetVelocityConstraintsOpts(conf),
560✔
816
                                  conf.doStats? &m_statsResource: conf.upstream),
560✔
817
    m_proxyKeysResource(GetProxyKeysOpts(conf), conf.doStats? &m_statsResource: conf.upstream),
560✔
818
    m_islandResource({conf.reserveBuffers}, conf.doStats? &m_statsResource: conf.upstream),
560✔
819
    m_tree(conf.treeCapacity),
560✔
820
    m_vertexRadius{conf.vertexRadius}
560✔
821
{
822
    m_proxiesForContacts.reserve(conf.proxyCapacity);
560✔
823
    m_contactBuffer.reserve(conf.contactCapacity);
560✔
824
    m_manifoldBuffer.reserve(conf.contactCapacity);
560✔
825
    m_contacts.reserve(conf.contactCapacity);
560✔
826
    m_islanded.contacts.reserve(conf.contactCapacity);
560✔
827
}
560✔
828

829
AabbTreeWorld::AabbTreeWorld(const AabbTreeWorld& other):
12✔
830
    m_statsResource(other.m_statsResource.upstream_resource()),
12✔
831
    m_bodyConstraintsResource(other.m_bodyConstraintsResource.GetOptions(),
24✔
832
                              other.m_statsResource.upstream_resource()?
12✔
833
                              &m_statsResource: other.m_bodyConstraintsResource.GetUpstream()),
12✔
834
    m_positionConstraintsResource(other.m_positionConstraintsResource.GetOptions(),
24✔
835
                                  other.m_statsResource.upstream_resource()?
12✔
836
                                  &m_statsResource: other.m_positionConstraintsResource.GetUpstream()),
12✔
837
    m_velocityConstraintsResource(other.m_velocityConstraintsResource.GetOptions(),
24✔
838
                                  other.m_statsResource.upstream_resource()?
12✔
839
                                  &m_statsResource: other.m_velocityConstraintsResource.GetUpstream()),
12✔
840
    m_proxyKeysResource(other.m_proxyKeysResource.GetOptions(),
24✔
841
                        other.m_statsResource.upstream_resource()?
12✔
842
                        &m_statsResource: other.m_proxyKeysResource.GetUpstream()),
12✔
843
    m_islandResource(other.m_islandResource.GetOptions(),
24✔
844
                     other.m_statsResource.upstream_resource()?
12✔
845
                     &m_statsResource: other.m_islandResource.GetUpstream()),
12✔
846
    m_tree(other.m_tree),
12✔
847
    m_bodyBuffer(other.m_bodyBuffer),
12✔
848
    m_shapeBuffer(other.m_shapeBuffer),
12✔
849
    m_jointBuffer(other.m_jointBuffer),
12✔
850
    m_contactBuffer(other.m_contactBuffer),
12✔
851
    m_manifoldBuffer(other.m_manifoldBuffer),
12✔
852
    m_bodyContacts(other.m_bodyContacts),
12✔
853
    m_bodyJoints(other.m_bodyJoints),
12✔
854
    m_bodyProxies(other.m_bodyProxies),
12✔
855
    m_proxiesForContacts(other.m_proxiesForContacts),
12✔
856
    m_fixturesForProxies(other.m_fixturesForProxies),
12✔
857
    m_bodiesForSync(other.m_bodiesForSync),
12✔
858
    m_bodies(other.m_bodies),
12✔
859
    m_joints(other.m_joints),
12✔
860
    m_contacts(other.m_contacts),
12✔
861
    m_islanded(other.m_islanded),
12✔
862
    m_listeners(other.m_listeners),
12✔
863
    m_flags(other.m_flags),
12✔
864
    m_inv_dt0(other.m_inv_dt0),
12✔
865
    m_vertexRadius(other.m_vertexRadius)
12✔
866
{
867
}
12✔
868

869
AabbTreeWorld::AabbTreeWorld(AabbTreeWorld&& other) noexcept:
458✔
870
    m_statsResource(other.m_statsResource.upstream_resource()),
458✔
871
    m_bodyConstraintsResource(other.m_bodyConstraintsResource.GetOptions(),
914✔
872
                              other.m_statsResource.upstream_resource()?
458✔
873
                              &m_statsResource: other.m_bodyConstraintsResource.GetUpstream()),
456✔
874
    m_positionConstraintsResource(other.m_positionConstraintsResource.GetOptions(),
914✔
875
                                  other.m_statsResource.upstream_resource()?
458✔
876
                                  &m_statsResource: other.m_positionConstraintsResource.GetUpstream()),
456✔
877
    m_velocityConstraintsResource(other.m_velocityConstraintsResource.GetOptions(),
914✔
878
                                  other.m_statsResource.upstream_resource()?
458✔
879
                                  &m_statsResource: other.m_velocityConstraintsResource.GetUpstream()),
456✔
880
    m_proxyKeysResource(other.m_proxyKeysResource.GetOptions(),
914✔
881
                        other.m_statsResource.upstream_resource()?
458✔
882
                        &m_statsResource: other.m_proxyKeysResource.GetUpstream()),
456✔
883
    m_islandResource(other.m_islandResource.GetOptions(),
914✔
884
                     other.m_statsResource.upstream_resource()?
458✔
885
                     &m_statsResource: other.m_islandResource.GetUpstream()),
456✔
886
    m_tree(std::move(other.m_tree)),
458✔
887
    m_bodyBuffer(std::move(other.m_bodyBuffer)),
458✔
888
    m_shapeBuffer(std::move(other.m_shapeBuffer)),
458✔
889
    m_jointBuffer(std::move(other.m_jointBuffer)),
458✔
890
    m_contactBuffer(std::move(other.m_contactBuffer)),
458✔
891
    m_manifoldBuffer(std::move(other.m_manifoldBuffer)),
458✔
892
    m_bodyContacts(std::move(other.m_bodyContacts)),
458✔
893
    m_bodyJoints(std::move(other.m_bodyJoints)),
458✔
894
    m_bodyProxies(std::move(other.m_bodyProxies)),
458✔
895
    m_proxiesForContacts(std::move(other.m_proxiesForContacts)),
458✔
896
    m_fixturesForProxies(std::move(other.m_fixturesForProxies)),
458✔
897
    m_bodiesForSync(std::move(other.m_bodiesForSync)),
458✔
898
    m_bodies(std::move(other.m_bodies)),
458✔
899
    m_joints(std::move(other.m_joints)),
458✔
900
    m_contacts(std::move(other.m_contacts)),
458✔
901
    m_islanded(std::move(other.m_islanded)),
458✔
902
    m_listeners(std::move(other.m_listeners)),
458✔
903
    m_flags(other.m_flags),
458✔
904
    m_inv_dt0(other.m_inv_dt0),
458✔
905
    m_vertexRadius(other.m_vertexRadius)
458✔
906
{
907
}
458✔
908

909
AabbTreeWorld::~AabbTreeWorld() noexcept
1,030✔
910
{
911
    Clear(*this);
1,030✔
912
}
1,030✔
913

914
bool operator==(const AabbTreeWorld& lhs, const AabbTreeWorld& rhs) noexcept
40✔
915
{
916
    return // newline!
917
        // skip m_tree, should just be a cache anyway
918
        (lhs.m_bodyBuffer == rhs.m_bodyBuffer) && // newline!
76✔
919
        (lhs.m_shapeBuffer == rhs.m_shapeBuffer) && // newline!
68✔
920
        (lhs.m_jointBuffer == rhs.m_jointBuffer) && // newline!
64✔
921
        (lhs.m_contactBuffer == rhs.m_contactBuffer) && // newline!
64✔
922
        (lhs.m_manifoldBuffer == rhs.m_manifoldBuffer) && // newline!
64✔
923
        (lhs.m_bodyContacts == rhs.m_bodyContacts) && // newline!
64✔
924
        (lhs.m_bodyJoints == rhs.m_bodyJoints) && // newline!
64✔
925
        (lhs.m_bodyProxies == rhs.m_bodyProxies) && // newline!
32✔
926
        (lhs.m_proxiesForContacts == rhs.m_proxiesForContacts) && // newline!
32✔
927
        (lhs.m_fixturesForProxies == rhs.m_fixturesForProxies) && // newline!
32✔
928
        (lhs.m_bodiesForSync == rhs.m_bodiesForSync) && // newline!
32✔
929
        (lhs.m_bodies == rhs.m_bodies) && // newline!
32✔
930
        (lhs.m_joints == rhs.m_joints) && // newline!
32✔
931
        (lhs.m_contacts == rhs.m_contacts) && // newline!
64✔
932
        (lhs.m_islanded == rhs.m_islanded) && // newline!
32✔
933
        // skip m_listeners, they're inconsequential & not very comparable anyway
934
        (lhs.m_flags == rhs.m_flags) && // newline!
32✔
935
        (lhs.m_inv_dt0 == rhs.m_inv_dt0) && // newline!
108✔
936
        (lhs.m_vertexRadius == rhs.m_vertexRadius);
72✔
937
}
938

939
bool operator!=(const AabbTreeWorld& lhs, const AabbTreeWorld& rhs) noexcept
10✔
940
{
941
    return !(lhs == rhs);
10✔
942
}
943

944
void Clear(AabbTreeWorld& world) noexcept
1,500✔
945
{
946
    if (const auto listener = world.m_listeners.jointDestruction) {
1,500✔
947
        for_each(cbegin(world.m_joints), cend(world.m_joints), [&listener](const auto& id) {
14✔
948
            try {
949
                listener(id);
4✔
950
            }
951
            catch (...) // NOLINT(bugprone-empty-catch)
2✔
952
            {
953
                // Don't allow exception to escape.
954
            }
955
        });
4✔
956
    }
1,500✔
957
    if (const auto listener = world.m_listeners.shapeDestruction) {
1,500✔
958
        for (auto&& shape: world.m_shapeBuffer) {
28✔
959
            if (shape != Shape{}) {
14✔
960
                using underlying_type = ::playrho::underlying_type_t<ShapeID>;
961
                const auto index = &shape - world.m_shapeBuffer.data();
14✔
962
                try {
963
                    listener(static_cast<ShapeID>(static_cast<underlying_type>(index)));
14✔
964
                }
965
                catch (...) // NOLINT(bugprone-empty-catch)
4✔
966
                {
967
                    // Don't allow exception to escape.
968
                }
4✔
969
            }
970
        }
971
    }
1,500✔
972
    world.m_inv_dt0 = 0_Hz;
1,500✔
973
    world.m_flags = AabbTreeWorld::e_stepComplete;
1,500✔
974
    world.m_islanded.bodies.clear();
1,500✔
975
    world.m_islanded.joints.clear();
1,500✔
976
    world.m_islanded.contacts.clear();
1,500✔
977
    world.m_contacts.clear();
1,500✔
978
    world.m_joints.clear();
1,500✔
979
    world.m_bodies.clear();
1,500✔
980
    world.m_bodiesForSync.clear();
1,500✔
981
    world.m_fixturesForProxies.clear();
1,500✔
982
    world.m_proxiesForContacts.clear();
1,500✔
983
    world.m_tree.Clear();
1,500✔
984
    world.m_manifoldBuffer.clear();
1,500✔
985
    world.m_contactBuffer.clear();
1,500✔
986
    world.m_jointBuffer.clear();
1,500✔
987
    world.m_bodyBuffer.clear();
1,500✔
988
    world.m_shapeBuffer.clear();
1,500✔
989
    world.m_bodyProxies.clear();
1,500✔
990
    world.m_bodyContacts.clear();
1,500✔
991
    world.m_bodyJoints.clear();
1,500✔
992
}
1,500✔
993

994
BodyCounter GetBodyRange(const AabbTreeWorld& world) noexcept
72✔
995
{
996
    return static_cast<BodyCounter>(world.m_bodyBuffer.size());
72✔
997
}
998

999
JointCounter GetJointRange(const AabbTreeWorld& world) noexcept
30✔
1000
{
1001
    return static_cast<JointCounter>(world.m_jointBuffer.size());
30✔
1002
}
1003

1004
ContactCounter GetContactRange(const AabbTreeWorld& world) noexcept
12✔
1005
{
1006
    return static_cast<ContactCounter>(world.m_contactBuffer.size());
12✔
1007
}
1008

1009
BodyID CreateBody(AabbTreeWorld& world, Body body)
156,491✔
1010
{
1011
    if (IsLocked(world)) {
156,491✔
1012
        throw WrongState(worldIsLockedMsg);
3,339✔
1013
    }
1014
    if (size(world.m_bodies) >= MaxBodies) {
153,152✔
1015
        throw LengthError("CreateBody: operation would exceed MaxBodies");
2✔
1016
    }
1017
    Validate(world.m_shapeBuffer, Span<const ShapeID>(body.GetShapes()), noSuchShapeMsg);
153,150✔
1018
    const auto id = static_cast<BodyID>(
1019
        static_cast<BodyID::underlying_type>(world.m_bodyBuffer.Allocate(std::move(body))));
153,148✔
1020
    world.m_islanded.bodies.resize(size(world.m_bodyBuffer));
153,148✔
1021
    const auto bodyContactsIndex = world.m_bodyContacts.Allocate();
153,148✔
1022
    static constexpr auto DefaultBodyContactsReserveSize = 32u;
1023
    world.m_bodyContacts[bodyContactsIndex].reserve(DefaultBodyContactsReserveSize);
153,148✔
1024
    world.m_bodyJoints.Allocate();
153,148✔
1025
    const auto bodyProxiesIndex = world.m_bodyProxies.Allocate();
153,148✔
1026
    world.m_bodyProxies[bodyProxiesIndex].reserve(1u);
153,148✔
1027
    world.m_bodies.push_back(id);
153,148✔
1028
    const auto& bufferedBody = world.m_bodyBuffer[to_underlying(id)];
153,148✔
1029
    if (IsEnabled(bufferedBody)) {
153,148✔
1030
        Append(world.m_fixturesForProxies, id, bufferedBody.GetShapes());
153,146✔
1031
    }
1032
    return id;
153,148✔
1033
}
1034

1035
void AabbTreeWorld::Remove(BodyID id)
40✔
1036
{
1037
    m_bodiesForSync.erase(remove(begin(m_bodiesForSync), end(m_bodiesForSync), id),
40✔
1038
                             end(m_bodiesForSync));
40✔
1039
    const auto it = find(cbegin(m_bodies), cend(m_bodies), id);
40✔
1040
    if (it != cend(m_bodies)) {
40✔
1041
        // Remove in reverse order from add/allocate.
1042
        m_bodies.erase(it);
40✔
1043
        m_bodyProxies.Free(to_underlying(id));
40✔
1044
        m_bodyJoints.Free(to_underlying(id));
40✔
1045
        m_bodyContacts.Free(to_underlying(id));
40✔
1046
        m_bodyBuffer.Free(to_underlying(id));
40✔
1047
        m_islanded.bodies.resize(size(m_bodyBuffer));
40✔
1048
    }
1049
}
40✔
1050

1051
void Destroy(AabbTreeWorld& world, BodyID id)
3,381✔
1052
{
1053
    if (IsLocked(world)) {
3,381✔
1054
        throw WrongState(worldIsLockedMsg);
3,339✔
1055
    }
1056

1057
    const auto& body = GetBody(world, id);
42✔
1058

1059
    // Delete the attached joints.
1060
    auto& joints = world.m_bodyJoints[to_underlying(id)];
40✔
1061
    while (!joints.empty()) {
42✔
1062
        const auto jointID = std::get<JointID>(*begin(joints));
2✔
1063
        if (world.m_listeners.jointDestruction) {
2✔
1064
            world.m_listeners.jointDestruction(jointID);
2✔
1065
        }
1066
        const auto endIter = cend(world.m_joints);
2✔
1067
        const auto iter = find(cbegin(world.m_joints), endIter, jointID);
2✔
1068
        if (iter != endIter) {
2✔
1069
            world.Remove(jointID); // removes joint from body!
2✔
1070
            world.m_joints.erase(iter);
2✔
1071
            world.m_jointBuffer.Free(to_underlying(jointID));
2✔
1072
        }
1073
    }
1074

1075
    // Destroy the attached contacts.
1076
    Erase(world.m_bodyContacts[to_underlying(id)], [&world,&body](ContactID contactID) {
40✔
1077
        world.Destroy(contactID, &body);
6✔
1078
        return true;
6✔
1079
    });
1080

1081
    for (auto&& shapeId: body.GetShapes()) {
56✔
1082
        EraseAll(world.m_fixturesForProxies, std::make_pair(id, shapeId));
16✔
1083
    }
1084

1085
    const auto proxies = FindProxies(world.m_tree, id);
40✔
1086
    for (const auto& proxy: proxies) {
52✔
1087
        world.m_tree.DestroyLeaf(proxy);
12✔
1088
    }
1089
    if (world.m_listeners.detach) {
40✔
1090
        for (const auto& shapeId: body.GetShapes()) {
8✔
1091
            world.m_listeners.detach(std::make_pair(id, shapeId));
4✔
1092
        }
1093
    }
1094
    world.Remove(id);
40✔
1095
}
40✔
1096

1097
bool IsDestroyed(const AabbTreeWorld& world, BodyID id) noexcept
16✔
1098
{
1099
    return world.m_bodyBuffer.FindFree(to_underlying(id));
16✔
1100
}
1101

1102
void SetJoint(AabbTreeWorld& world, JointID id, Joint def)
211,403✔
1103
{
1104
    if (IsLocked(world)) {
211,403✔
1105
        throw WrongState(worldIsLockedMsg);
3,339✔
1106
    }
1107
    // Validate the references...
1108
    auto &joint = At(world.m_jointBuffer, id, noSuchJointMsg);
208,064✔
1109
    if (const auto bodyId = GetBodyA(def); bodyId != InvalidBodyID) {
208,062✔
1110
        GetBody(world, bodyId);
208,060✔
1111
    }
1112
    if (const auto bodyId = GetBodyB(def); bodyId != InvalidBodyID) {
208,060✔
1113
        GetBody(world, bodyId);
208,058✔
1114
    }
1115
    if (world.m_jointBuffer.FindFree(to_underlying(id))) {
208,058✔
1116
        throw InvalidArgument(idIsDestroyedMsg);
2✔
1117
    }
1118
    world.Remove(id);
208,056✔
1119
    joint = std::move(def);
208,056✔
1120
    world.Add(id, !GetCollideConnected(joint));
208,056✔
1121
}
208,056✔
1122

1123
JointID CreateJoint(AabbTreeWorld& world, Joint def)
134,585✔
1124
{
1125
    if (IsLocked(world)) {
134,585✔
1126
        throw WrongState(worldIsLockedMsg);
3,339✔
1127
    }
1128
    if (size(world.m_joints) >= MaxJoints) {
131,246✔
1129
        throw LengthError("CreateJoint: operation would exceed MaxJoints");
2✔
1130
    }
1131
    // Validate the referenced bodies...
1132
    if (const auto bodyId = GetBodyA(def); bodyId != InvalidBodyID) {
131,244✔
1133
        GetBody(world, bodyId);
131,240✔
1134
    }
1135
    if (const auto bodyId = GetBodyB(def); bodyId != InvalidBodyID) {
131,242✔
1136
        GetBody(world, bodyId);
131,240✔
1137
    }
1138
    const auto id = static_cast<JointID>(
1139
        static_cast<JointID::underlying_type>(world.m_jointBuffer.Allocate(std::move(def))));
131,242✔
1140
    world.m_islanded.joints.resize(size(world.m_jointBuffer));
131,242✔
1141
    world.m_joints.push_back(id);
131,242✔
1142
    // Note: creating a joint doesn't wake the bodies.
1143
    world.Add(id, !GetCollideConnected(world.m_jointBuffer[to_underlying(id)]));
131,242✔
1144
    return id;
131,242✔
1145
}
1146

1147
void AabbTreeWorld::Add(JointID id, bool flagForFiltering)
339,298✔
1148
{
1149
    const auto& joint = m_jointBuffer[to_underlying(id)];
339,298✔
1150
    const auto bodyA = GetBodyA(joint);
339,298✔
1151
    const auto bodyB = GetBodyB(joint);
339,298✔
1152
    if (bodyA != InvalidBodyID) {
339,298✔
1153
        m_bodyJoints[to_underlying(bodyA)].emplace_back(bodyB, id);
339,294✔
1154
    }
1155
    if (bodyB != InvalidBodyID) {
339,298✔
1156
        m_bodyJoints[to_underlying(bodyB)].emplace_back(bodyA, id);
339,296✔
1157
    }
1158
    if (flagForFiltering && (bodyA != InvalidBodyID) && (bodyB != InvalidBodyID)) {
339,298✔
1159
        if (FlagForFiltering(m_contactBuffer, bodyA, m_bodyContacts[to_underlying(bodyB)], bodyB)) {
339,282✔
1160
            m_flags |= e_needsContactFiltering;
2✔
1161
        }
1162
    }
1163
}
339,298✔
1164

1165
void AabbTreeWorld::Remove(JointID id)
208,066✔
1166
{
1167
    // Disconnect from island graph.
1168
    const auto& joint = m_jointBuffer[to_underlying(id)];
208,066✔
1169
    const auto bodyIdA = GetBodyA(joint);
208,066✔
1170
    const auto bodyIdB = GetBodyB(joint);
208,066✔
1171
    const auto collideConnected = GetCollideConnected(joint);
208,066✔
1172

1173
    // If the joint prevented collisions, then flag any contacts for filtering.
1174
    if ((!collideConnected) && (bodyIdA != InvalidBodyID) && (bodyIdB != InvalidBodyID)) {
208,066✔
1175
        if (FlagForFiltering(m_contactBuffer, bodyIdA, m_bodyContacts[to_underlying(bodyIdB)], bodyIdB)) {
208,064✔
1176
            m_flags |= e_needsContactFiltering;
2✔
1177
        }
1178
    }
1179

1180
    // Wake up connected bodies.
1181
    if (bodyIdA != InvalidBodyID) {
208,066✔
1182
        auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
208,064✔
1183
        SetAwake(bodyA);
208,064✔
1184
        auto& bodyJoints = m_bodyJoints[to_underlying(bodyIdA)];
208,064✔
1185
        const auto found = FindTypeValue(bodyJoints, id);
208,064✔
1186
        assert(found);
208,064✔
1187
        if (found) {
208,064✔
1188
            bodyJoints.erase(*found);
208,064✔
1189
        }
1190
    }
1191
    if (bodyIdB != InvalidBodyID) {
208,066✔
1192
        auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
208,064✔
1193
        SetAwake(bodyB);
208,064✔
1194
        auto& bodyJoints = m_bodyJoints[to_underlying(bodyIdB)];
208,064✔
1195
        const auto found = FindTypeValue(bodyJoints, id);
208,064✔
1196
        assert(found);
208,064✔
1197
        if (found) {
208,064✔
1198
            bodyJoints.erase(*found);
208,064✔
1199
        }
1200
    }
1201
}
208,066✔
1202

1203
void Destroy(AabbTreeWorld& world, JointID id)
293✔
1204
{
1205
    if (IsLocked(world)) {
293✔
1206
        throw WrongState(worldIsLockedMsg);
285✔
1207
    }
1208
    const auto endIter = cend(world.m_joints);
8✔
1209
    const auto iter = find(cbegin(world.m_joints), endIter, id);
8✔
1210
    if (iter != endIter) {
8✔
1211
        world.Remove(id);
8✔
1212
        world.m_joints.erase(iter);
8✔
1213
        world.m_jointBuffer.Free(to_underlying(id));
8✔
1214
    }
1215
}
8✔
1216

1217
bool IsDestroyed(const AabbTreeWorld& world, JointID id) noexcept
4✔
1218
{
1219
    return world.m_jointBuffer.FindFree(to_underlying(id));
4✔
1220
}
1221

1222
ShapeCounter GetShapeRange(const AabbTreeWorld& world) noexcept
54✔
1223
{
1224
    return static_cast<ShapeCounter>(size(world.m_shapeBuffer));
54✔
1225
}
1226

1227
ShapeID CreateShape(AabbTreeWorld& world, Shape def)
138,739✔
1228
{
1229
    const auto vertexRadius = GetVertexRadiusInterval(world);
138,739✔
1230
    const auto childCount = GetChildCount(def);
138,739✔
1231
    for (auto i = ChildCounter{0}; i < childCount; ++i) {
277,480✔
1232
        const auto vr = GetVertexRadius(def, i);
138,745✔
1233
        if (!(vr >= vertexRadius.GetMin())) {
138,745✔
1234
            throw InvalidArgument("CreateShape: vertex radius < min");
2✔
1235
        }
1236
        if (!(vr <= vertexRadius.GetMax())) {
138,743✔
1237
            throw InvalidArgument("CreateShape: vertex radius > max");
2✔
1238
        }
1239
    }
1240
    if (IsLocked(world)) {
138,735✔
1241
        throw WrongState(worldIsLockedMsg);
3,339✔
1242
    }
1243
    if (size(world.m_shapeBuffer) >= MaxShapes) {
135,396✔
1244
        throw LengthError("CreateShape: operation would exceed MaxShapes");
2✔
1245
    }
1246
    return static_cast<ShapeID>(static_cast<ShapeID::underlying_type>(world.m_shapeBuffer.Allocate(std::move(def))));
135,394✔
1247
}
1248

1249
void Destroy(AabbTreeWorld& world, ShapeID id)
3,363✔
1250
{
1251
    if (IsLocked(world)) {
3,363✔
1252
        throw WrongState(worldIsLockedMsg);
3,339✔
1253
    }
1254
    At(world.m_shapeBuffer, id, noSuchShapeMsg); // confirm id valid.
24✔
1255
    const auto numBodies = GetBodyRange(world);
20✔
1256
    for (auto bodyIdx = static_cast<decltype(GetBodyRange(world))>(0); bodyIdx < numBodies; ++bodyIdx) {
24✔
1257
        auto body = world.m_bodyBuffer[bodyIdx];
4✔
1258
        auto n = std::size_t(0);
4✔
1259
        while (body.Detach(id)) {
6✔
1260
            ++n;
2✔
1261
        }
1262
        if (n) {
4✔
1263
            SetBody(world, BodyID(bodyIdx), body);
2✔
1264
        }
1265
    }
4✔
1266
    world.m_shapeBuffer.Free(to_underlying(id));
20✔
1267
}
20✔
1268

1269
bool IsDestroyed(const AabbTreeWorld& world, ShapeID id) noexcept
8✔
1270
{
1271
    return world.m_shapeBuffer.FindFree(to_underlying(id));
8✔
1272
}
1273

1274
const Shape& GetShape(const AabbTreeWorld& world, ShapeID id)
25,041,008✔
1275
{
1276
    return At(world.m_shapeBuffer, id, noSuchShapeMsg);
25,041,008✔
1277
}
1278

1279
void SetShape(AabbTreeWorld& world, ShapeID id, Shape def) // NOLINT(readability-function-cognitive-complexity)
3,391✔
1280
{
1281
    if (IsLocked(world)) {
3,391✔
1282
        throw WrongState(worldIsLockedMsg);
3,339✔
1283
    }
1284
    auto& shape = At(world.m_shapeBuffer, id, noSuchShapeMsg);
52✔
1285
    if (world.m_shapeBuffer.FindFree(to_underlying(id))) {
50✔
1286
        throw InvalidArgument(idIsDestroyedMsg);
2✔
1287
    }
1288
    const auto geometryChanged = IsGeomChanged(shape, def);
48✔
1289
    for (auto&& b: world.m_bodyBuffer) {
62✔
1290
        if (!Find(b.GetShapes(), id)) {
14✔
1291
            continue;
×
1292
        }
1293
        SetAwake(b);
14✔
1294
        if (geometryChanged && IsEnabled(b)) {
14✔
1295
            const auto bodyId = BodyID(static_cast<BodyID::underlying_type>(&b - data(world.m_bodyBuffer)));
2✔
1296
            auto& bodyProxies = world.m_bodyProxies[to_underlying(bodyId)];
2✔
1297
            const auto lastProxy = end(bodyProxies);
2✔
1298
            bodyProxies.erase(std::remove_if(begin(bodyProxies), lastProxy,
2✔
1299
                                             [&world,id](DynamicTree::Size idx){
8✔
1300
                const auto leafData = world.m_tree.GetLeafData(idx);
4✔
1301
                if (leafData.shapeId == id) {
4✔
1302
                    world.m_tree.DestroyLeaf(idx);
2✔
1303
                    EraseFirst(world.m_proxiesForContacts, idx);
2✔
1304
                    return true;
2✔
1305
                }
1306
                return false;
2✔
1307
            }), lastProxy);
1308
            // Destroy any contacts associated with the fixture.
1309
            Erase(world.m_bodyContacts[to_underlying(bodyId)], [&world,bodyId,id,&b](ContactID contactID) {
2✔
1310
                if (!IsFor(world.m_contactBuffer[to_underlying(contactID)], bodyId, id)) {
×
1311
                    return false;
×
1312
                }
1313
                world.Destroy(contactID, &b);
×
1314
                return true;
×
1315
            });
1316
            const auto fixture = std::make_pair(bodyId, id);
2✔
1317
            EraseAll(world.m_fixturesForProxies, fixture);
2✔
1318
            DestroyProxies(world.m_tree, bodyId, id, world.m_proxiesForContacts);
2✔
1319
            world.m_fixturesForProxies.push_back(fixture);
2✔
1320
        }
1321
    }
1322
    if (GetFilter(shape) != GetFilter(def)) {
48✔
1323
        auto anyNeedFiltering = false;
4✔
1324
        for (auto& c: world.m_contactBuffer) {
6✔
1325
            if (IsFor(c, id)) {
2✔
1326
                FlagForFiltering(c);
2✔
1327
                SetAwake(world.m_bodyBuffer, c);
2✔
1328
                anyNeedFiltering = true;
2✔
1329
            }
1330
        }
1331
        if (anyNeedFiltering) {
4✔
1332
            world.m_flags |= AabbTreeWorld::e_needsContactFiltering;
2✔
1333
        }
1334
        ForMatchingProxies(world.m_tree, id, [&](DynamicTreeSize proxyId){
4✔
1335
            world.m_proxiesForContacts.push_back(proxyId);
4✔
1336
        });
4✔
1337
    }
1338
    if ((IsSensor(shape) != IsSensor(def)) || (GetFriction(shape) != GetFriction(def)) ||
122✔
1339
        (GetRestitution(shape) != GetRestitution(def)) || geometryChanged) {
122✔
1340
        for (auto&& c: world.m_contactBuffer) {
32✔
1341
            if (IsFor(c, id)) {
4✔
1342
                FlagForUpdating(c);
4✔
1343
                SetAwake(world.m_bodyBuffer, c);
4✔
1344
            }
1345
        }
1346
    }
1347
    shape = std::move(def);
48✔
1348
}
48✔
1349

1350
void AabbTreeWorld::AddToIsland(Island& island, BodyID seedID,
463,329✔
1351
                            BodyCounter& remNumBodies,
1352
                            ContactCounter& remNumContacts,
1353
                            JointCounter& remNumJoints)
1354
{
1355
#ifndef NDEBUG
1356
    assert(!m_islanded.bodies[to_underlying(seedID)]);
463,329✔
1357
    auto& seed = m_bodyBuffer[to_underlying(seedID)];
463,329✔
1358
    assert(IsSpeedable(seed));
463,329✔
1359
    assert(IsAwake(seed));
463,329✔
1360
    assert(IsEnabled(seed));
463,329✔
1361
    assert(remNumBodies != 0);
463,329✔
1362
    assert(remNumBodies < MaxBodies);
463,329✔
1363
#endif
1364
    // Perform a depth first search (DFS) on the constraint graph.
1365
    // Create a stack for bodies to be is-in-island that aren't already in the island.
1366
    auto stack = BodyStack{&m_bodyStackResource};
463,329✔
1367
    stack.reserve(remNumBodies);
463,329✔
1368
    stack.push_back(seedID);
463,329✔
1369
    m_islanded.bodies[to_underlying(seedID)] = true;
463,329✔
1370
    AddToIsland(island, stack, remNumBodies, remNumContacts, remNumJoints);
463,329✔
1371
}
463,329✔
1372

1373
void AabbTreeWorld::AddToIsland(Island& island, BodyStack& stack,
463,329✔
1374
                            BodyCounter& remNumBodies,
1375
                            ContactCounter& remNumContacts,
1376
                            JointCounter& remNumJoints)
1377
{
1378
    while (!empty(stack)) {
3,498,500✔
1379
        // Grab the next body off the stack and add it to the island.
1380
        const auto bodyID = stack.back();
3,035,171✔
1381
        stack.pop_back();
3,035,171✔
1382

1383
        auto& body = m_bodyBuffer[to_underlying(bodyID)];
3,035,171✔
1384

1385
        assert(IsEnabled(body));
3,035,171✔
1386
        island.bodies.push_back(bodyID);
3,035,171✔
1387
        assert(remNumBodies > 0);
3,035,171✔
1388
        --remNumBodies;
3,035,171✔
1389

1390
        // Don't propagate islands across bodies that can't have a velocity (static bodies).
1391
        // This keeps islands smaller and helps with isolating separable collision clusters.
1392
        if (!IsSpeedable(body)) {
3,035,171✔
1393
            continue;
7,678✔
1394
        }
1395

1396
        // Make sure the body is awake (without resetting sleep timer).
1397
        body.SetAwakeFlag();
3,027,493✔
1398

1399
        const auto oldNumContacts = size(island.contacts);
3,027,493✔
1400
        // Adds appropriate contacts of current body and appropriate 'other' bodies of those contacts.
1401
        AddContactsToIsland(island, stack, m_bodyContacts[to_underlying(bodyID)], bodyID);
3,027,493✔
1402

1403
        const auto newNumContacts = size(island.contacts);
3,027,493✔
1404
        assert(newNumContacts >= oldNumContacts);
3,027,493✔
1405
        const auto netNumContacts = newNumContacts - oldNumContacts;
3,027,493✔
1406
        assert(remNumContacts >= netNumContacts);
3,027,493✔
1407
        remNumContacts -= static_cast<ContactCounter>(netNumContacts);
3,027,493✔
1408

1409
        const auto numJoints = size(island.joints);
3,027,493✔
1410
        // Adds appropriate joints of current body and appropriate 'other' bodies of those joint.
1411
        AddJointsToIsland(island, stack, m_bodyJoints[to_underlying(bodyID)]);
3,027,493✔
1412

1413
        remNumJoints -= static_cast<JointCounter>(size(island.joints) - numJoints);
3,027,493✔
1414
    }
1415
}
463,329✔
1416

1417
void AabbTreeWorld::AddContactsToIsland(Island& island, BodyStack& stack,
3,027,493✔
1418
                                    const BodyContactIDs& contacts,
1419
                                    BodyID bodyID)
1420
{
1421
    for_each(cbegin(contacts), cend(contacts), [&](const auto& ci) {
3,027,493✔
1422
        const auto contactID = std::get<ContactID>(ci);
11,767,392✔
1423
        if (!m_islanded.contacts[to_underlying(contactID)]) {
11,767,392✔
1424
            const auto& contact = m_contactBuffer[to_underlying(contactID)];
7,070,434✔
1425
            if (IsEnabled(contact) && IsTouching(contact) && !IsSensor(contact)) {
7,070,434✔
1426
                const auto other = GetOtherBody(contact, bodyID);
4,959,967✔
1427
                island.contacts.push_back(contactID);
4,959,967✔
1428
                m_islanded.contacts[to_underlying(contactID)] = true;
4,959,967✔
1429
                if (!m_islanded.bodies[to_underlying(other)]) {
4,959,967✔
1430
                    m_islanded.bodies[to_underlying(other)] = true;
2,569,685✔
1431
                    stack.push_back(other);
2,569,685✔
1432
                }
1433
            }
1434
        }
1435
    });
11,767,392✔
1436
}
3,027,493✔
1437

1438
void AabbTreeWorld::AddJointsToIsland(Island& island, BodyStack& stack,
3,027,493✔
1439
                                  const BodyJointIDs& joints)
1440
{
1441
    for_each(cbegin(joints), cend(joints), [this,&island,&stack](const auto& ji) {
3,027,493✔
1442
        const auto jointID = std::get<JointID>(ji);
212,382✔
1443
        assert(jointID != InvalidJointID);
212,382✔
1444
        if (!m_islanded.joints[to_underlying(jointID)]) {
212,382✔
1445
            const auto otherID = std::get<BodyID>(ji);
210,197✔
1446
            const auto other = (otherID == InvalidBodyID)
210,197✔
1447
                                   ? static_cast<Body*>(nullptr)
210,197✔
1448
                                   : &m_bodyBuffer[to_underlying(otherID)];
210,195✔
1449
            assert(!other || IsEnabled(*other) || !IsAwake(*other));
210,197✔
1450
            if (!other || IsEnabled(*other))
210,197✔
1451
            {
1452
                m_islanded.joints[to_underlying(jointID)] = true;
2,197✔
1453
                island.joints.push_back(jointID);
2,197✔
1454
                if ((otherID != InvalidBodyID) && !m_islanded.bodies[to_underlying(otherID)])
2,197✔
1455
                {
1456
                    m_islanded.bodies[to_underlying(otherID)] = true;
2,157✔
1457
                    stack.push_back(otherID);
2,157✔
1458
                }
1459
            }
1460
        }
1461
    });
212,382✔
1462
}
3,027,493✔
1463

1464
RegStepStats AabbTreeWorld::SolveReg(const StepConf& conf)
222,553✔
1465
{
1466
    auto stats = RegStepStats{};
222,553✔
1467
    auto remNumBodies = static_cast<BodyCounter>(size(m_bodies)); // Remaining # of bodies.
222,553✔
1468
    auto remNumContacts = static_cast<ContactCounter>(size(m_contacts)); // Remaining # of contacts.
222,553✔
1469
    auto remNumJoints = static_cast<JointCounter>(size(m_joints)); // Remaining # of joints.
222,553✔
1470

1471
    // Clear all the island flags.
1472
    // This builds the logical set of bodies, contacts, and joints eligible for resolution.
1473
    // As bodies, contacts, or joints get added to resolution islands, they're essentially
1474
    // removed from this eligible set.
1475
    ResizeAndReset(m_islanded.bodies, size(m_bodyBuffer), false);
222,553✔
1476
    ResizeAndReset(m_islanded.contacts, size(m_contactBuffer), false);
222,553✔
1477
    ResizeAndReset(m_islanded.joints, size(m_jointBuffer), false);
222,553✔
1478

1479
#if defined(DO_THREADED)
1480
    std::vector<std::future<IslandStats>> futures;
1481
    futures.reserve(remNumBodies);
1482
#endif
1483
    // Build and simulate all awake islands.
1484
    for (const auto& b: m_bodies) {
4,303,230✔
1485
        if (!m_islanded.bodies[to_underlying(b)]) {
4,080,677✔
1486
            auto& body = m_bodyBuffer[to_underlying(b)];
1,516,515✔
1487
            assert(!IsAwake(body) || IsSpeedable(body));
1,516,515✔
1488
            if (IsAwake(body) && IsEnabled(body)) {
1,516,515✔
1489
                ++stats.islandsFound;
463,329✔
1490
                Island island{m_islandResource, m_islandResource, m_islandResource};
463,329✔
1491
                // Size the island for the remaining un-evaluated contacts.
1492
                island.bodies.reserve(remNumBodies);
463,329✔
1493
                island.contacts.reserve(remNumContacts);
463,329✔
1494
                island.joints.reserve(remNumJoints);
463,329✔
1495
                AddToIsland(island, b, remNumBodies, remNumContacts, remNumJoints);
463,329✔
1496
#if defined(DO_SORT_ISLANDS)
1497
                Sort(island);
1498
#endif
1499
                stats.maxIslandBodies = std::max(stats.maxIslandBodies,
926,658✔
1500
                                                 static_cast<BodyCounter>(size(island.bodies)));
463,329✔
1501
                const auto numRemoved = RemoveUnspeedablesFromIslanded(island.bodies, m_bodyBuffer, m_islanded.bodies);
463,329✔
1502
                remNumBodies += static_cast<BodyCounter>(numRemoved);
463,329✔
1503
#if defined(DO_THREADED)
1504
                // Updates bodies' sweep.pos0 to current sweep.pos1 and bodies' sweep.pos1 to new positions
1505
                futures.push_back(std::async(std::launch::async, &AabbTreeWorld::SolveRegIslandViaGS,
1506
                                             this, conf, island));
1507
#else
1508
                const auto solverResults = SolveRegIslandViaGS(conf, island);
463,329✔
1509
                ::playrho::Update(stats, solverResults);
463,329✔
1510
#endif
1511
            }
463,329✔
1512
        }
1513
    }
1514

1515
#if defined(DO_THREADED)
1516
    for (auto&& future: futures) {
1517
        const auto solverResults = future.get();
1518
        ::playrho::Update(stats, solverResults);
1519
    }
1520
#endif
1521

1522
    for (const auto& bodyId: m_bodies) {
4,303,230✔
1523
        if (m_islanded.bodies[to_underlying(bodyId)]) {
4,080,677✔
1524
            // A non-static body that was in an island may have moved.
1525
            const auto& body = m_bodyBuffer[to_underlying(bodyId)];
3,027,493✔
1526
            if (IsSpeedable(body)) {
3,027,493✔
1527
                // Update fixtures (for broad-phase).
1528
                stats.proxiesMoved += Synchronize(m_bodyProxies[to_underlying(bodyId)],
3,027,493✔
1529
                                                  GetTransform0(GetSweep(body)),
6,054,986✔
1530
                                                  GetTransformation(body),
1531
                                                  conf.displaceMultiplier, conf.aabbExtension);
3,027,493✔
1532
            }
1533
        }
1534
    }
1535

1536
    // Look for new contacts.
1537
    stats.contactsAdded = AddContacts(FindContacts(m_proxyKeysResource, m_tree, std::move(m_proxiesForContacts)));
222,553✔
1538
    m_proxiesForContacts = {};
222,553✔
1539
    return stats;
445,106✔
1540
}
1541

1542
IslandStats AabbTreeWorld::SolveRegIslandViaGS(const StepConf& conf, const Island& island)
463,329✔
1543
{
1544
    assert(!empty(island.bodies) || !empty(island.contacts) || !empty(island.joints));
463,329✔
1545
    
1546
    auto results = IslandStats{};
463,329✔
1547
    results.positionIters = conf.regPositionIters;
463,329✔
1548
    const auto h = conf.deltaTime; ///< Time step.
463,329✔
1549

1550
    // Update bodies' pos0 values.
1551
    for_each(cbegin(island.bodies), cend(island.bodies), [&](const auto& bodyID) {
463,329✔
1552
        auto& body = m_bodyBuffer[to_underlying(bodyID)];
3,035,171✔
1553
        SetPosition0(body, GetPosition1(body));
3,035,171✔
1554
        // XXX/TODO figure out why the following causes Gears Test to stutter!!!
1555
        // SetSweep(body, GetNormalized(GetSweep(body)));
1556
        // SetSweep(body, Sweep{GetNormalized(GetPosition0(body)), GetLocalCenter(body)});
1557
    });
3,035,171✔
1558

1559
    // Copy bodies' pos1 and velocity data into local arrays.
1560
    auto bodyConstraints = GetBodyConstraints(m_bodyConstraintsResource,
1561
                                              island.bodies, m_bodyBuffer, h, GetMovementConf(conf));
463,329✔
1562
    auto posConstraints = GetPositionConstraints(m_positionConstraintsResource, island.contacts,
463,329✔
1563
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer);
463,329✔
1564
    auto velConstraints = GetVelocityConstraints(m_velocityConstraintsResource, island.contacts,
463,329✔
1565
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer,
463,329✔
1566
                                                 bodyConstraints,
1567
                                                 GetRegVelocityConstraintConf(conf));
463,329✔
1568
    if (conf.doWarmStart) {
463,329✔
1569
        WarmStartVelocities(velConstraints, bodyConstraints);
463,315✔
1570
    }
1571

1572
    const auto psConf = GetRegConstraintSolverConf(conf);
463,329✔
1573

1574
    for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
463,329✔
1575
        auto& joint = m_jointBuffer[to_underlying(id)];
2,197✔
1576
        InitVelocity(joint, bodyConstraints, conf, psConf);
2,197✔
1577
    });
2,197✔
1578
    
1579
    results.velocityIters = conf.regVelocityIters;
463,329✔
1580
    for (auto i = decltype(conf.regVelocityIters){0}; i < conf.regVelocityIters; ++i) {
533,135✔
1581
        auto jointsOkay = true;
523,259✔
1582
        for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
523,259✔
1583
            auto& joint = m_jointBuffer[to_underlying(id)];
16,234✔
1584
            jointsOkay &= SolveVelocity(joint, bodyConstraints, conf);
16,234✔
1585
        });
16,234✔
1586
        // Note that the new incremental impulse can potentially be orders of magnitude
1587
        // greater than the last incremental impulse used in this loop.
1588
        const auto newIncImpulse = SolveVelocityConstraintsViaGS(velConstraints, bodyConstraints);
523,259✔
1589
        results.maxIncImpulse = std::max(results.maxIncImpulse, newIncImpulse);
523,259✔
1590
        if (jointsOkay && (newIncImpulse <= conf.regMinMomentum)) {
523,259✔
1591
            // No joint related velocity constraints were out of tolerance.
1592
            // No body related velocity constraints were out of tolerance.
1593
            // There does not appear to be any benefit to doing more loops now.
1594
            // XXX: Is it really safe to bail now? Not certain of that.
1595
            // Bail now assuming that this is helpful to do...
1596
            results.velocityIters = i + 1;
453,453✔
1597
            break;
453,453✔
1598
        }
1599
    }
1600
    
1601
    // updates array of tentative new body positions per the velocities as if there were no obstacles...
1602
    IntegratePositions(island.bodies, bodyConstraints, h);
463,329✔
1603
    
1604
    // Solve position constraints
1605
    for (auto i = decltype(conf.regPositionIters){0}; i < conf.regPositionIters; ++i) {
487,325✔
1606
        const auto minSeparation = SolvePositionConstraintsViaGS(posConstraints, bodyConstraints,
478,372✔
1607
                                                                 psConf);
478,372✔
1608
        results.minSeparation = std::min(results.minSeparation, minSeparation);
478,372✔
1609
        const auto contactsOkay = (minSeparation >= conf.regMinSeparation);
478,372✔
1610
        auto jointsOkay = true;
478,372✔
1611
        for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
478,372✔
1612
            auto& joint = m_jointBuffer[to_underlying(id)];
2,305✔
1613
            jointsOkay &= SolvePosition(joint, bodyConstraints, psConf);
2,305✔
1614
        });
2,305✔
1615
        if (contactsOkay && jointsOkay) {
478,372✔
1616
            // Reached tolerance, early out...
1617
            results.positionIters = i + 1;
454,376✔
1618
            results.solved = true;
454,376✔
1619
            break;
454,376✔
1620
        }
1621
    }
1622
    
1623
    // Update normal and tangent impulses of contacts' manifold points
1624
    for_each(cbegin(velConstraints), cend(velConstraints), [&](const VelocityConstraint& vc) {
463,329✔
1625
        const auto i = static_cast<VelocityConstraints::size_type>(&vc - data(velConstraints));
4,959,967✔
1626
        AssignImpulses(m_manifoldBuffer[to_underlying(island.contacts[i])], vc);
4,959,967✔
1627
    });
4,959,967✔
1628

1629
    for (const auto& id: island.bodies) {
3,498,500✔
1630
        const auto i = to_underlying(id);
3,035,171✔
1631
        const auto& bc = bodyConstraints[i];
3,035,171✔
1632
        auto& body = m_bodyBuffer[i];
3,035,171✔
1633
        // Could normalize position here to avoid unbounded angles but angular
1634
        // normalization isn't handled correctly by joints that constrain rotation.
1635
        body.JustSetVelocity(bc.GetVelocity());
3,035,171✔
1636
        // XXX/TODO figure out why calling GetNormalized here causes Gears Test to stutter!!!
1637
        if (const auto pos = /*GetNormalized*/(bc.GetPosition()); GetPosition1(body) != pos) {
3,035,171✔
1638
            SetPosition1(body, pos);
2,609,422✔
1639
            FlagForUpdating(m_contactBuffer, m_bodyContacts[i]);
2,609,422✔
1640
        }
1641
    }
1642

1643
    // XXX: Should contacts needing updating be updated now??
1644

1645
    if (m_listeners.postSolveContact) {
463,329✔
1646
        Report(m_listeners.postSolveContact, island.contacts, velConstraints,
209,933✔
1647
               results.solved? results.positionIters - 1: StepConf::InvalidIteration);
209,933✔
1648
    }
1649
    
1650
    results.bodiesSlept = BodyCounter{0};
463,329✔
1651
    const auto minUnderActiveTime = UpdateUnderActiveTimes(island.bodies, m_bodyBuffer, conf);
463,329✔
1652
    if ((minUnderActiveTime >= conf.minStillTimeToSleep) && results.solved) {
463,329✔
1653
        results.bodiesSlept = static_cast<decltype(results.bodiesSlept)>(Sleepem(island.bodies,
38✔
1654
                                                                                 m_bodyBuffer));
38✔
1655
    }
1656

1657
    return results;
926,658✔
1658
}
463,329✔
1659

1660
AabbTreeWorld::UpdateContactsData
1661
AabbTreeWorld::UpdateContactTOIs(const StepConf& conf)
262,218✔
1662
{
1663
    auto results = UpdateContactsData{};
262,218✔
1664

1665
    const auto toiConf = GetToiConf(conf);
262,218✔
1666
    for (const auto& contact: m_contacts)
57,868,373✔
1667
    {
1668
        auto& c = m_contactBuffer[to_underlying(std::get<ContactID>(contact))];
57,606,155✔
1669
        if (HasValidToi(c))
57,606,155✔
1670
        {
1671
            ++results.numValidTOI;
2,695,515✔
1672
            continue;
55,575,467✔
1673
        }
1674
        if (!IsEnabled(c) || IsSensor(c) || !IsImpenetrable(c))
54,910,640✔
1675
        {
1676
            continue;
52,879,419✔
1677
        }
1678
        if (GetToiCount(c) >= conf.maxSubSteps)
2,031,221✔
1679
        {
1680
            // What are the pros/cons of this?
1681
            // Larger m_maxSubSteps slows down the simulation.
1682
            // m_maxSubSteps of 44 and higher seems to decrease the occurrance of tunneling
1683
            // of multiple bullet body collisions with static objects.
1684
            ++results.numAtMaxSubSteps;
3✔
1685
            continue;
3✔
1686
        }
1687

1688
        auto& bA = m_bodyBuffer[to_underlying(GetBodyA(c))];
2,031,218✔
1689
        auto& bB = m_bodyBuffer[to_underlying(GetBodyB(c))];
2,031,218✔
1690

1691
        if (!IsAwake(bA) && !IsAwake(bB)) {
2,031,218✔
1692
            continue;
530✔
1693
        }
1694

1695
        /*
1696
         * Put the sweeps onto the same time interval.
1697
         * Presumably no unresolved collisions happen before the maximum of the bodies'
1698
         * alpha-0 times. So long as the least TOI of the contacts is always the first
1699
         * collision that gets dealt with, this presumption is safe.
1700
         */
1701
        const auto alpha0 = std::max(GetSweep(bA).alpha0, GetSweep(bB).alpha0);
2,030,688✔
1702
        Advance0(bA, alpha0);
2,030,688✔
1703
        Advance0(bB, alpha0);
2,030,688✔
1704

1705
        // Compute the TOI for this contact (one or both bodies are awake and impenetrable).
1706
        // Computes the time of impact in interval [0, 1]
1707
        const auto proxyA = GetChild(m_shapeBuffer[to_underlying(GetShapeA(c))], GetChildIndexA(c));
2,030,688✔
1708
        const auto proxyB = GetChild(m_shapeBuffer[to_underlying(GetShapeB(c))], GetChildIndexB(c));
2,030,688✔
1709

1710
        // Large rotations can make the root finder of TimeOfImpact fail, so normalize sweep angles.
1711
        const auto sweepA = GetNormalized(GetSweep(bA));
2,030,688✔
1712
        const auto sweepB = GetNormalized(GetSweep(bB));
2,030,688✔
1713

1714
        // Compute the TOI for this contact (one or both bodies are awake and impenetrable).
1715
        // Computes the time of impact in interval [0, 1]
1716
        const auto output = GetToiViaSat(proxyA, sweepA, proxyB, sweepB, toiConf);
2,030,688✔
1717

1718
        // Use Min function to handle floating point imprecision which possibly otherwise
1719
        // could provide a TOI that's greater than 1.
1720
        const auto toi = IsValidForTime(output.state)?
2,102,647✔
1721
            std::min(alpha0 + (Real(1) - alpha0) * output.time, Real(1)): Real(1);
71,959✔
1722
        SetToi(c, {UnitIntervalFF<Real>(toi)});
2,030,688✔
1723
        results.maxDistIters = std::max(results.maxDistIters, output.stats.max_dist_iters);
2,030,688✔
1724
        results.maxToiIters = std::max(results.maxToiIters, output.stats.toi_iters);
2,030,688✔
1725
        results.maxRootIters = std::max(results.maxRootIters, output.stats.max_root_iters);
2,030,688✔
1726
        ++results.numUpdatedTOI;
2,030,688✔
1727
    }
1728

1729
    return results;
262,218✔
1730
}
1731

1732
ToiStepStats AabbTreeWorld::SolveToi(const StepConf& conf)
222,557✔
1733
{
1734
    auto stats = ToiStepStats{};
222,557✔
1735

1736
    if (IsStepComplete(*this)) {
222,557✔
1737
        ResetBodiesForSolveTOI(m_bodies, m_bodyBuffer);
222,553✔
1738
        Unset(m_islanded.bodies, m_bodies);
222,553✔
1739
        ResetContactsForSolveTOI(m_contactBuffer, m_contacts);
222,553✔
1740
        Unset(m_islanded.contacts, m_contacts);
222,553✔
1741
    }
1742

1743
    const auto subStepping = GetSubStepping(*this);
222,557✔
1744

1745
    // Find TOI events and solve them.
1746
    for (;;) {
1747
        const auto updateData = UpdateContactTOIs(conf);
262,218✔
1748
        stats.contactsAtMaxSubSteps += updateData.numAtMaxSubSteps;
262,218✔
1749
        stats.contactsUpdatedToi += updateData.numUpdatedTOI;
262,218✔
1750
        stats.maxDistIters = std::max(stats.maxDistIters, updateData.maxDistIters);
262,218✔
1751
        stats.maxRootIters = std::max(stats.maxRootIters, updateData.maxRootIters);
262,218✔
1752
        stats.maxToiIters = std::max(stats.maxToiIters, updateData.maxToiIters);
262,218✔
1753
        
1754
        const auto next = GetSoonestContact(m_contacts, m_contactBuffer);
262,218✔
1755
        if (next == InvalidContactID) {
262,218✔
1756
            // No more TOI events to handle within the current time step. Done!
1757
            m_flags |= e_stepComplete;
222,553✔
1758
            break;
222,553✔
1759
        }
1760

1761
        ++stats.contactsFound;
39,665✔
1762
        auto islandsFound = 0u;
39,665✔
1763
        if (!m_islanded.contacts[to_underlying(next)]) {
39,665✔
1764
            const auto solverResults = SolveToi(next, conf);
39,665✔
1765
            stats.minSeparation = std::min(stats.minSeparation, solverResults.minSeparation);
39,665✔
1766
            stats.maxIncImpulse = std::max(stats.maxIncImpulse, solverResults.maxIncImpulse);
39,665✔
1767
            stats.islandsSolved += solverResults.solved;
39,665✔
1768
            stats.sumPosIters += solverResults.positionIters;
39,665✔
1769
            stats.sumVelIters += solverResults.velocityIters;
39,665✔
1770
            if ((solverResults.positionIters > 0) || (solverResults.velocityIters > 0)) {
39,665✔
1771
                ++islandsFound;
39,641✔
1772
            }
1773
            stats.contactsUpdatedTouching += solverResults.contactsUpdated;
39,665✔
1774
            stats.contactsSkippedTouching += solverResults.contactsSkipped;
39,665✔
1775
        }
1776
        stats.islandsFound += islandsFound;
39,665✔
1777

1778
        // Reset island flags and synchronize broad-phase proxies.
1779
        for (const auto& bodyId: m_bodies) {
24,073,932✔
1780
            if (m_islanded.bodies[to_underlying(bodyId)]) {
24,034,267✔
1781
                m_islanded.bodies[to_underlying(bodyId)] = false;
39,691✔
1782
                const auto& body = m_bodyBuffer[to_underlying(bodyId)];
39,691✔
1783
                if (IsAccelerable(body)) {
39,691✔
1784
                    stats.proxiesMoved += Synchronize(m_bodyProxies[to_underlying(bodyId)],
39,691✔
1785
                                                      GetTransform0(body.GetSweep()),
39,691✔
1786
                                                      GetTransformation(body),
1787
                                                      conf.displaceMultiplier, conf.aabbExtension);
39,691✔
1788
                    const auto& bodyContacts = m_bodyContacts[to_underlying(bodyId)];
39,691✔
1789
                    ResetBodyContactsForSolveTOI(m_contactBuffer, bodyContacts);
39,691✔
1790
                    Unset(m_islanded.contacts, bodyContacts);
39,691✔
1791
                }
1792
            }
1793
        }
1794

1795
        // Commit fixture proxy movements to the broad-phase so that new contacts are created.
1796
        // Also, some contacts can be destroyed.
1797
        stats.contactsAdded += AddContacts(FindContacts(m_proxyKeysResource, m_tree, std::move(m_proxiesForContacts)));
39,665✔
1798
        m_proxiesForContacts = {};
39,665✔
1799

1800
        if (subStepping) {
39,665✔
1801
            m_flags &= ~e_stepComplete;
4✔
1802
            break;
4✔
1803
        }
1804
    }
39,661✔
1805
    return stats;
222,557✔
1806
}
1807

1808
IslandStats AabbTreeWorld::SolveToi(ContactID contactID, const StepConf& conf)
39,665✔
1809
{
1810
    // Note:
1811
    //   This function is what used to be b2World::SolveToi(const b2TimeStep& step).
1812
    //   It also differs internally from Erin's implementation.
1813
    //   Here's some specific behavioral differences:
1814
    //   1. Bodies don't get their under-active times reset (like they do in Erin's code).
1815

1816
    auto numUpdated = ContactCounter{0};
39,665✔
1817
    auto numSkipped = ContactCounter{0};
39,665✔
1818
    auto& contact = m_contactBuffer[to_underlying(contactID)];
39,665✔
1819

1820
    /*
1821
     * Confirm that contact is as it's supposed to be according to contract of the
1822
     * GetSoonestContact function from which this contact should have been obtained.
1823
     */
1824
    assert(IsEnabled(contact));
39,665✔
1825
    assert(!IsSensor(contact));
39,665✔
1826
    assert(IsImpenetrable(contact));
39,665✔
1827
    assert(!m_islanded.contacts[to_underlying(contactID)]);
39,665✔
1828
    assert(GetToi(contact));
39,665✔
1829

1830
    const auto toi = ZeroToUnderOneFF<Real>(*GetToi(contact)); // NOLINT(bugprone-unchecked-optional-access)
39,665✔
1831
    const auto bodyIdA = GetBodyA(contact);
39,665✔
1832
    const auto bodyIdB = GetBodyB(contact);
39,665✔
1833
    auto& bA = m_bodyBuffer[to_underlying(bodyIdA)];
39,665✔
1834
    auto& bB = m_bodyBuffer[to_underlying(bodyIdB)];
39,665✔
1835

1836
    {
1837
        const auto backupA = GetSweep(bA);
39,665✔
1838
        const auto backupB = GetSweep(bB);
39,665✔
1839

1840
        // Advance the bodies to the TOI.
1841
        assert((toi != Real(0)) || ((GetSweep(bA).alpha0 == Real(0)) && (GetSweep(bB).alpha0 == Real(0))));
39,665✔
1842
        Advance(bA, toi);
39,665✔
1843
        Advance(bB, toi);
39,665✔
1844
        FlagForUpdating(m_contactBuffer, m_bodyContacts[to_underlying(bodyIdA)]);
39,665✔
1845
        FlagForUpdating(m_contactBuffer, m_bodyContacts[to_underlying(bodyIdB)]);
39,665✔
1846

1847
        // The TOI contact likely has some new contact points.
1848
        SetEnabled(contact);
39,665✔
1849
        assert(contact.NeedsUpdating());
39,665✔
1850
        Update(contactID, GetUpdateConf(conf));
39,665✔
1851
        ++numUpdated;
39,665✔
1852

1853
        SetToi(contact, {});
39,665✔
1854
        contact.IncrementToiCount();
39,665✔
1855

1856
        // Is contact disabled or separated?
1857
        //
1858
        // XXX: Not often, but sometimes, contact.IsTouching() is false now.
1859
        //      Seems like this is a bug, or at least suboptimal, condition.
1860
        //      This function shouldn't be getting called unless contact has an
1861
        //      impact indeed at the given TOI. Seen this happen in an edge-polygon
1862
        //      contact situation where the polygon had a larger than default
1863
        //      vertex radius. CollideShapes had called GetManifoldFaceB which
1864
        //      was failing to see 2 clip points after GetClipPoints was called.
1865
        //assert(contact.IsEnabled() && contact.IsTouching());
1866
        if (!IsEnabled(contact) || !IsTouching(contact)) {
39,665✔
1867
            //contact.UnsetEnabled();
1868
            SetSweep(bA, backupA);
×
1869
            SetSweep(bB, backupB);
×
1870
            return IslandStats{}.IncContactsUpdated(numUpdated).IncContactsSkipped(numSkipped);
×
1871
        }
1872
    }
1873
    if (IsSpeedable(bA)) {
39,665✔
1874
        bA.SetAwakeFlag();
30✔
1875
        // XXX should the body's under-active time be reset here?
1876
        //   Erin's code does for here but not in b2World::Solve(const b2TimeStep& step).
1877
        //   Reseting the body's under-active time has performance implications.
1878
    }
1879
    if (IsSpeedable(bB)) {
39,665✔
1880
        bB.SetAwakeFlag();
39,661✔
1881
        // XXX should the body's under-active time be reset here?
1882
        //   Erin's code does for here but not in b2World::Solve(const b2TimeStep& step).
1883
        //   Reseting the body's under-active time has performance implications.
1884
    }
1885

1886
    // Build the island
1887
    Island island{m_islandResource, m_islandResource, m_islandResource};
39,665✔
1888
    island.bodies.reserve(size(m_bodies));
39,665✔
1889
    island.contacts.reserve(used(m_contactBuffer));
39,665✔
1890

1891
     // These asserts get triggered sometimes if contacts within TOI are iterated over.
1892
    assert(!m_islanded.bodies[to_underlying(bodyIdA)]);
39,665✔
1893
    assert(!m_islanded.bodies[to_underlying(bodyIdB)]);
39,665✔
1894
    m_islanded.bodies[to_underlying(bodyIdA)] = true;
39,665✔
1895
    m_islanded.bodies[to_underlying(bodyIdB)] = true;
39,665✔
1896
    m_islanded.contacts[to_underlying(contactID)] = true;
39,665✔
1897
    island.bodies.push_back(bodyIdA);
39,665✔
1898
    island.bodies.push_back(bodyIdB);
39,665✔
1899
    island.contacts.push_back(contactID);
39,665✔
1900

1901
    // Process the contacts of the two bodies, adding appropriate ones to the island,
1902
    // adding appropriate other bodies of added contacts, and advancing those other
1903
    // bodies sweeps and transforms to the minimum contact's TOI.
1904
    if (IsAccelerable(bA)) {
39,665✔
1905
        const auto procOut = ProcessContactsForTOI(bodyIdA, island, toi, conf);
30✔
1906
        numUpdated += procOut.contactsUpdated;
30✔
1907
        numSkipped += procOut.contactsSkipped;
30✔
1908
    }
1909
    if (IsAccelerable(bB)) {
39,665✔
1910
        const auto procOut = ProcessContactsForTOI(bodyIdB, island, toi, conf);
39,661✔
1911
        numUpdated += procOut.contactsUpdated;
39,661✔
1912
        numSkipped += procOut.contactsSkipped;
39,661✔
1913
    }
1914

1915
#if defined(DO_SORT_ISLANDS)
1916
    Sort(island);
1917
#endif
1918
    RemoveUnspeedablesFromIslanded(island.bodies, m_bodyBuffer, m_islanded.bodies);
39,665✔
1919

1920
    // Now solve for remainder of time step.
1921
    auto subConf = StepConf{conf};
39,665✔
1922
    subConf.deltaTime = (Real(1) - toi) * conf.deltaTime;
39,665✔
1923
    return SolveToiViaGS(island, subConf).IncContactsUpdated(numUpdated).IncContactsSkipped(numSkipped);
39,665✔
1924
}
39,665✔
1925

1926
IslandStats AabbTreeWorld::SolveToiViaGS(const Island& island, const StepConf& conf)
39,665✔
1927
{
1928
    auto results = IslandStats{};
39,665✔
1929

1930
    /*
1931
     * Resets body constraints to what they were right after reg phase processing.
1932
     * Presumably the regular phase resolution has already taken care of updating the
1933
     * body's velocity w.r.t. acceleration and damping such that this call here to get
1934
     * the body constraint doesn't need to pass an elapsed time (and doesn't need to
1935
     * update the velocity from what it already is).
1936
     */
1937
    auto bodyConstraints = GetBodyConstraints(m_bodyConstraintsResource,
1938
                                              island.bodies, m_bodyBuffer, 0_s, GetMovementConf(conf));
39,665✔
1939

1940
    // Initialize the body state.
1941
    auto posConstraints = GetPositionConstraints(m_positionConstraintsResource, island.contacts, m_contactBuffer,
39,665✔
1942
                                                 m_manifoldBuffer, m_shapeBuffer);
39,665✔
1943

1944
    // Solve TOI-based position constraints.
1945
    assert(results.minSeparation == std::numeric_limits<Length>::infinity());
39,665✔
1946
    assert(results.solved == false);
39,665✔
1947
    results.positionIters = conf.toiPositionIters;
39,665✔
1948
    {
1949
        const auto psConf = GetToiConstraintSolverConf(conf);
39,665✔
1950
        for (auto i = decltype(conf.toiPositionIters){0}; i < conf.toiPositionIters; ++i) {
96,919✔
1951
            //
1952
            // Note: There are two flavors of the SolvePositionConstraints function.
1953
            //   One takes an extra two arguments that are the indexes of two bodies that are
1954
            //   okay to move. The other one does not.
1955
            //   Calling the selective solver (that takes the two additional arguments) appears
1956
            //   to result in phsyics simulations that are more prone to tunneling. Meanwhile,
1957
            //   using the non-selective solver would presumably be slower (since it appears to
1958
            //   have more that it will do). Assuming that slower is preferable to tunnelling,
1959
            //   then the non-selective function is the one to be calling here.
1960
            //
1961
            const auto minSeparation = SolvePositionConstraintsViaGS(posConstraints,
96,895✔
1962
                                                                     bodyConstraints, psConf);
96,895✔
1963
            results.minSeparation = std::min(results.minSeparation, minSeparation);
96,895✔
1964
            if (minSeparation >= conf.toiMinSeparation) {
96,895✔
1965
                // Reached tolerance, early out...
1966
                results.positionIters = i + 1;
39,641✔
1967
                results.solved = true;
39,641✔
1968
                break;
39,641✔
1969
            }
1970
        }
1971
    }
1972

1973
    // Leap of faith to new safe state.
1974
    // Not doing this results in slower simulations.
1975
    // Originally this update was only done to island.bodies 0 and 1.
1976
    // Unclear whether rest of bodies should also be updated. No difference noticed.
1977
    for (const auto& id: island.bodies) {
118,995✔
1978
        const auto& bc = bodyConstraints[to_underlying(id)];
79,330✔
1979
        SetPosition0(m_bodyBuffer[to_underlying(id)], bc.GetPosition());
79,330✔
1980
    }
1981

1982
    auto velConstraints = GetVelocityConstraints(m_velocityConstraintsResource, island.contacts,
39,665✔
1983
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer,
39,665✔
1984
                                                 bodyConstraints,
1985
                                                 GetToiVelocityConstraintConf(conf));
39,665✔
1986

1987
    // No warm starting is needed for TOI events because warm
1988
    // starting impulses were applied in the discrete solver.
1989

1990
    // Solve velocity constraints.
1991
    assert(results.maxIncImpulse == 0_Ns);
39,665✔
1992
    results.velocityIters = conf.toiVelocityIters;
39,665✔
1993
    for (auto i = decltype(conf.toiVelocityIters){0}; i < conf.toiVelocityIters; ++i) {
262,639✔
1994
        const auto newIncImpulse = SolveVelocityConstraintsViaGS(velConstraints, bodyConstraints);
235,191✔
1995
        if (newIncImpulse <= conf.toiMinMomentum) {
235,191✔
1996
            // No body related velocity constraints were out of tolerance.
1997
            // There does not appear to be any benefit to doing more loops now.
1998
            // XXX: Is it really safe to bail now? Not certain of that.
1999
            // Bail now assuming that this is helpful to do...
2000
            results.velocityIters = i + 1;
12,217✔
2001
            break;
12,217✔
2002
        }
2003
        results.maxIncImpulse = std::max(results.maxIncImpulse, newIncImpulse);
222,974✔
2004
    }
2005

2006
    // Don't store TOI contact forces for warm starting because they can be quite large.
2007

2008
    IntegratePositions(island.bodies, bodyConstraints, conf.deltaTime);
39,665✔
2009
    for (const auto& id: island.bodies) {
118,995✔
2010
        const auto i = to_underlying(id);
79,330✔
2011
        auto& body = m_bodyBuffer[i];
79,330✔
2012
        auto& bc = bodyConstraints[i];
79,330✔
2013
        body.JustSetVelocity(bc.GetVelocity());
79,330✔
2014
        if (const auto pos = bc.GetPosition(); GetPosition1(body) != pos) {
79,330✔
2015
            SetPosition1(body, pos);
39,691✔
2016
            FlagForUpdating(m_contactBuffer, m_bodyContacts[i]);
39,691✔
2017
        }
2018
    }
2019

2020
    if (m_listeners.postSolveContact) {
39,665✔
2021
        Report(m_listeners.postSolveContact, island.contacts, velConstraints, results.positionIters);
3,332✔
2022
    }
2023

2024
    return results;
79,330✔
2025
}
39,665✔
2026

2027
AabbTreeWorld::ProcessContactsOutput
2028
AabbTreeWorld::ProcessContactsForTOI( // NOLINT(readability-function-cognitive-complexity)
39,691✔
2029
                                     BodyID id, Island& island, ZeroToUnderOneFF<Real> toi,
2030
                                     const StepConf& conf)
2031
{
2032
    const auto& body = m_bodyBuffer[to_underlying(id)];
39,691✔
2033

2034
    assert(m_islanded.bodies[to_underlying(id)]);
39,691✔
2035
    assert(IsAccelerable(body));
39,691✔
2036

2037
    auto results = ProcessContactsOutput{};
39,691✔
2038
    assert(results.contactsUpdated == 0);
39,691✔
2039
    assert(results.contactsSkipped == 0);
39,691✔
2040
    
2041
    const auto updateConf = GetUpdateConf(conf);
39,691✔
2042

2043
    // Note: the original contact (for body of which this function was called) already is-in-island.
2044
    const auto bodyImpenetrable = IsImpenetrable(body);
39,691✔
2045
    for (const auto& ci: m_bodyContacts[to_underlying(id)]) {
196,451✔
2046
        const auto contactID = std::get<ContactID>(ci);
156,760✔
2047
        if (!m_islanded.contacts[to_underlying(contactID)]) {
156,760✔
2048
            auto& contact = m_contactBuffer[to_underlying(contactID)];
117,069✔
2049
            if (!IsSensor(contact)) {
117,069✔
2050
                const auto otherId = GetOtherBody(contact, id);
117,069✔
2051
                auto& other = m_bodyBuffer[to_underlying(otherId)];
117,069✔
2052
                if (bodyImpenetrable || IsImpenetrable(other)) {
117,069✔
2053
                    const auto otherIslanded = m_islanded.bodies[to_underlying(otherId)];
44,731✔
2054
                    {
2055
                        const auto backup = GetSweep(other);
44,731✔
2056
                        if (!otherIslanded /* && GetSweep(other).alpha0 != toi */) {
44,731✔
2057
                            Advance(other, toi);
3,302✔
2058
                            FlagForUpdating(m_contactBuffer, m_bodyContacts[to_underlying(otherId)]);
3,302✔
2059
                        }
2060

2061
                        // Update the contact points
2062
                        SetEnabled(contact);
44,731✔
2063
                        if (NeedsUpdating(contact)) {
44,731✔
2064
                            Update(contactID, updateConf);
44,731✔
2065
                            ++results.contactsUpdated;
44,731✔
2066
                        }
2067
                        else {
2068
                            ++results.contactsSkipped;
×
2069
                        }
2070

2071
                        // Revert and skip if contact disabled by user or not touching anymore (very possible).
2072
                        if (!IsEnabled(contact) || !IsTouching(contact)) {
44,731✔
2073
                            SetSweep(other, backup);
8,596✔
2074
                            continue;
8,596✔
2075
                        }
2076
                    }
2077
                    island.contacts.push_back(contactID);
36,135✔
2078
                    m_islanded.contacts[to_underlying(contactID)] = true;
36,135✔
2079
                    if (!otherIslanded) {
36,135✔
2080
                        if (IsSpeedable(other)) {
×
2081
                            other.SetAwakeFlag();
×
2082
                        }
2083
                        island.bodies.push_back(otherId);
×
2084
                        m_islanded.bodies[to_underlying(otherId)] = true;
×
2085
#if 0
2086
                        if (IsAccelerable(other)) {
2087
                            contactsUpdated += ProcessContactsForTOI(island, other, toi);
2088
                        }
2089
#endif
2090
                    }
2091
#ifndef NDEBUG
2092
                    else {
2093
                        /*
2094
                         * If other is-in-island but not in current island, then something's gone wrong.
2095
                         * Other needs to be in current island but was already in the island.
2096
                         * A previous contact island didn't grow to include all the bodies it needed or
2097
                         * perhaps the current contact is-touching while another one wasn't and the
2098
                         * inconsistency is throwing things off.
2099
                         */
2100
                        assert(Count(island, otherId) > 0);
36,135✔
2101
                    }
2102
#endif
2103
                }
2104
            }
2105
        }
2106
    }
2107
    return results;
39,691✔
2108
}
2109

2110
StepStats Step(AabbTreeWorld& world, const StepConf& conf)
226,104✔
2111
{
2112
    assert((world.m_vertexRadius.GetMax() * Real(2)) +
226,104✔
2113
           (conf.linearSlop / Real(4)) > (world.m_vertexRadius.GetMax() * Real(2)));
2114

2115
    if (IsLocked(world)) {
226,104✔
2116
        throw WrongState(worldIsLockedMsg);
3,339✔
2117
    }
2118

2119
    // "Named return value optimization" (NRVO) will make returning this more efficient.
2120
    auto stepStats = StepStats{};
222,765✔
2121
    {
2122
        const FlagGuard<decltype(world.m_flags)> flagGaurd(world.m_flags, AabbTreeWorld::e_locked);
222,765✔
2123

2124
        // Create proxies herein for access to conf.aabbExtension info!
2125
        for (const auto& [bodyID, shapeID]: world.m_fixturesForProxies) {
248,367✔
2126
            CreateProxies(world.m_tree, bodyID, shapeID, world.m_shapeBuffer[to_underlying(shapeID)],
25,602✔
2127
                          GetTransformation(world.m_bodyBuffer[to_underlying(bodyID)]),
25,602✔
2128
                          conf.aabbExtension,
25,602✔
2129
                          world.m_bodyProxies[to_underlying(bodyID)], world.m_proxiesForContacts);
25,602✔
2130
        }
2131
        world.m_fixturesForProxies = {};
222,765✔
2132

2133
        stepStats.pre.proxiesMoved = [&world](const StepConf& cfg){
668,295✔
2134
            auto proxiesMoved = PreStepStats::counter_type{0};
222,765✔
2135
            for_each(begin(world.m_bodiesForSync), end(world.m_bodiesForSync), [&world,&cfg,&proxiesMoved](const auto& bodyID) {
222,765✔
2136
                const auto xfm = GetTransformation(world.m_bodyBuffer[to_underlying(bodyID)]);
10✔
2137
                // Not always true: assert(GetTransform0(b->GetSweep()) == xfm);
2138
                proxiesMoved += world.Synchronize(world.m_bodyProxies[to_underlying(bodyID)], xfm, xfm,
10✔
2139
                                                  cfg.displaceMultiplier, cfg.aabbExtension);
10✔
2140
            });
10✔
2141
            return proxiesMoved;
222,765✔
2142
        }(conf);
222,765✔
2143
        world.m_bodiesForSync = {};
222,765✔
2144
        // pre.proxiesMoved is usually zero but sometimes isn't.
2145

2146
        {
2147
            // Note: this may update bodies (in addition to the contacts container).
2148
            const auto destroyStats = world.DestroyContacts(world.m_contacts);
222,765✔
2149
            stepStats.pre.destroyed = destroyStats.overlap + destroyStats.filter;
222,765✔
2150
        }
2151

2152
        // For any new fixtures added: need to find and create the new contacts.
2153
        // Note: this may update bodies (in addition to the contacts container).
2154
        stepStats.pre.added = world.AddContacts(FindContacts(world.m_proxyKeysResource, world.m_tree, std::move(world.m_proxiesForContacts)));
222,765✔
2155
        world.m_proxiesForContacts = {};
222,765✔
2156

2157
        if (conf.deltaTime != 0_s) {
222,765✔
2158
            world.m_inv_dt0 = (conf.deltaTime != 0_s)? Real(1) / conf.deltaTime: 0_Hz;
222,557✔
2159

2160
            // Could potentially run UpdateContacts multithreaded over split lists...
2161
            const auto updateStats = world.UpdateContacts(conf);
222,557✔
2162
            stepStats.pre.ignored = updateStats.ignored;
222,557✔
2163
            stepStats.pre.updated = updateStats.updated;
222,557✔
2164
            stepStats.pre.skipped = updateStats.skipped;
222,557✔
2165

2166
            // Integrate velocities, solve velocity constraints, and integrate positions.
2167
            if (IsStepComplete(world)) {
222,557✔
2168
                stepStats.reg = world.SolveReg(conf);
222,553✔
2169
            }
2170

2171
            // Handle TOI events.
2172
            if (conf.doToi) {
222,557✔
2173
                stepStats.toi = world.SolveToi(conf);
222,557✔
2174
            }
2175
        }
2176
    }
222,765✔
2177
    return stepStats;
222,765✔
2178
}
2179

2180
void ShiftOrigin(AabbTreeWorld& world, const Length2& newOrigin)
3,345✔
2181
{
2182
    if (IsLocked(world)) {
3,345✔
2183
        throw WrongState(worldIsLockedMsg);
3,339✔
2184
    }
2185

2186
    // Optimize for newOrigin being different than current...
2187
    for (const auto& body: world.m_bodies) {
22✔
2188
        auto& b = world.m_bodyBuffer[to_underlying(body)];
16✔
2189
        auto sweep = GetSweep(b);
16✔
2190
        sweep.pos0.linear -= newOrigin;
16✔
2191
        sweep.pos1.linear -= newOrigin;
16✔
2192
        SetSweep(b, sweep);
16✔
2193
        FlagForUpdating(world.m_contactBuffer, world.m_bodyContacts[to_underlying(body)]);
16✔
2194
    }
2195

2196
    for_each(begin(world.m_joints), end(world.m_joints), [&](const auto& joint) {
6✔
2197
        auto& j = world.m_jointBuffer[to_underlying(joint)];
2✔
2198
        ::playrho::d2::ShiftOrigin(j, newOrigin);
2✔
2199
    });
2✔
2200

2201
    world.m_tree.ShiftOrigin(newOrigin);
6✔
2202
}
6✔
2203

2204
void AabbTreeWorld::InternalDestroy(ContactID contactID, const Body* from)
844✔
2205
{
2206
    assert(contactID != InvalidContactID);
844✔
2207
    auto& contact = m_contactBuffer[to_underlying(contactID)];
844✔
2208
    if (m_listeners.endContact && contact.IsTouching()) {
844✔
2209
        // EndContact hadn't been called in DestroyOrUpdateContacts() since is-touching,
2210
        //  so call it now
2211
        m_listeners.endContact(contactID);
×
2212
    }
2213
    const auto bodyIdA = GetBodyA(contact);
844✔
2214
    const auto bodyIdB = GetBodyB(contact);
844✔
2215
    const auto bodyA = &m_bodyBuffer[to_underlying(bodyIdA)];
844✔
2216
    const auto bodyB = &m_bodyBuffer[to_underlying(bodyIdB)];
844✔
2217
    if (bodyA != from) {
844✔
2218
        auto& bodyContacts = m_bodyContacts[to_underlying(bodyIdA)];
838✔
2219
        if (const auto found = FindTypeValue(bodyContacts, contactID)) {
838✔
2220
            bodyContacts.erase(*found);
838✔
2221
        }
2222
    }
2223
    if (bodyB != from) {
844✔
2224
        auto& bodyContacts = m_bodyContacts[to_underlying(bodyIdB)];
844✔
2225
        if (const auto found = FindTypeValue(bodyContacts, contactID)) {
844✔
2226
            bodyContacts.erase(*found);
844✔
2227
        }
2228
    }
2229
    auto& manifold = m_manifoldBuffer[to_underlying(contactID)];
844✔
2230
    if ((manifold.GetPointCount() > 0) && !IsSensor(contact)) {
844✔
2231
        // Contact may have been keeping accelerable bodies of fixture A or B from moving.
2232
        // Need to awaken those bodies now in case they are again movable.
2233
        SetAwake(*bodyA);
6✔
2234
        SetAwake(*bodyB);
6✔
2235
    }
2236
    m_contactBuffer.Free(to_underlying(contactID));
844✔
2237
    m_manifoldBuffer.Free(to_underlying(contactID));
844✔
2238
}
844✔
2239

2240
void AabbTreeWorld::Destroy(ContactID contactID, const Body* from)
6✔
2241
{
2242
    assert(contactID != InvalidContactID);
6✔
2243
    if (const auto found = FindTypeValue(m_contacts, contactID)) {
6✔
2244
        m_contacts.erase(*found);
6✔
2245
    }
2246
    InternalDestroy(contactID, from);
6✔
2247
}
6✔
2248

2249
bool IsDestroyed(const AabbTreeWorld& world, ContactID id) noexcept
6✔
2250
{
2251
    return world.m_contactBuffer.FindFree(to_underlying(id));
6✔
2252
}
2253

2254
AabbTreeWorld::DestroyContactsStats AabbTreeWorld::DestroyContacts(KeyedContactIDs& contacts)
222,765✔
2255
{
2256
    auto stats = DestroyContactsStats{};
222,765✔
2257
    const auto beforeOverlapSize = size(contacts);
222,765✔
2258
    contacts.erase(std::remove_if(begin(contacts), end(contacts), [&](const auto& c){
222,765✔
2259
        const auto key = std::get<ContactKey>(c);
6,753,565✔
2260
        if (!TestOverlap(m_tree, key.GetMin(), key.GetMax())) {
6,753,565✔
2261
            // Destroy contacts that cease to overlap in the broad-phase.
2262
            InternalDestroy(std::get<ContactID>(c));
838✔
2263
            return true;
838✔
2264
        }
2265
        return false;
6,752,727✔
2266
    }), end(contacts));
222,765✔
2267
    const auto afterOverlapSize = size(contacts);
222,765✔
2268
    stats.overlap = static_cast<ContactCounter>(beforeOverlapSize - afterOverlapSize);
222,765✔
2269
    if (m_flags & e_needsContactFiltering) {
222,765✔
2270
        contacts.erase(std::remove_if(begin(contacts), end(contacts), [&](const auto& c){
2✔
2271
            const auto contactID = std::get<ContactID>(c);
2✔
2272
            auto& contact = m_contactBuffer[to_underlying(contactID)];
2✔
2273
            if (contact.NeedsFiltering()) {
2✔
2274
                const auto bodyIdA = GetBodyA(contact);
2✔
2275
                const auto bodyIdB = GetBodyB(contact);
2✔
2276
                const auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
2✔
2277
                const auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
2✔
2278
                const auto& shapeA = m_shapeBuffer[to_underlying(GetShapeA(contact))];
2✔
2279
                const auto& shapeB = m_shapeBuffer[to_underlying(GetShapeB(contact))];
2✔
2280
                if (!EitherIsAccelerable(bodyA, bodyB) ||
2✔
2281
                    !ShouldCollide(m_jointBuffer, m_bodyJoints, bodyIdA, bodyIdB) ||
4✔
2282
                    !ShouldCollide(shapeA, shapeB)) {
2✔
2283
                    InternalDestroy(contactID);
×
2284
                    return true;
×
2285
                }
2286
                contact.UnflagForFiltering();
2✔
2287
            }
2288
            return false;
2✔
2289
        }), end(contacts));
2✔
2290
        const auto afterFilteringSize = size(contacts);
2✔
2291
        stats.filter = static_cast<ContactCounter>(afterOverlapSize - afterFilteringSize);
2✔
2292
        m_flags &= ~e_needsContactFiltering;
2✔
2293
    }
2294
    return stats;
222,765✔
2295
}
2296

2297
AabbTreeWorld::UpdateContactsStats AabbTreeWorld::UpdateContacts(const StepConf& conf)
222,557✔
2298
{
2299
#ifdef DO_PAR_UNSEQ
2300
    atomic<std::uint32_t> ignored;
2301
    atomic<std::uint32_t> updated;
2302
    atomic<std::uint32_t> skipped;
2303
#else
2304
    auto ignored = std::uint32_t{0};
222,557✔
2305
    auto updated = std::uint32_t{0};
222,557✔
2306
    auto skipped = std::uint32_t{0};
222,557✔
2307
#endif
2308

2309
    const auto updateConf = GetUpdateConf(conf);
222,557✔
2310
    
2311
#if defined(DO_THREADED)
2312
    std::vector<ContactID> contactsNeedingUpdate;
2313
    contactsNeedingUpdate.reserve(size(m_contacts));
2314
    std::vector<std::future<void>> futures;
2315
    futures.reserve(size(m_contacts));
2316
#endif
2317

2318
    // Update awake contacts.
2319
    for_each(/*execution::par_unseq,*/ begin(m_contacts), end(m_contacts), [&](const auto& c) {
222,557✔
2320
        const auto contactID = std::get<ContactID>(c);
6,752,813✔
2321
        auto& contact = m_contactBuffer[to_underlying(contactID)];
6,752,813✔
2322
        const auto& bodyA = m_bodyBuffer[to_underlying(GetBodyA(contact))];
6,752,813✔
2323
        const auto& bodyB = m_bodyBuffer[to_underlying(GetBodyB(contact))];
6,752,813✔
2324

2325
        // Awake && speedable (dynamic or kinematic) means collidable.
2326
        // At least one body must be collidable
2327
        assert(!IsAwake(bodyA) || IsSpeedable(bodyA));
6,752,813✔
2328
        assert(!IsAwake(bodyB) || IsSpeedable(bodyB));
6,752,813✔
2329
        if (!IsAwake(bodyA) && !IsAwake(bodyB)) {
6,752,813✔
2330
            // This sometimes fails... is it important?
2331
            //assert(!contact.HasValidToi());
2332
            ++ignored;
20✔
2333
            return;
20✔
2334
        }
2335

2336
        // Possible that bodyA.GetSweep().alpha0 != 0
2337
        // Possible that bodyB.GetSweep().alpha0 != 0
2338

2339
        SetEnabled(contact);
6,752,793✔
2340

2341
        // Update the contact manifold and notify the listener.
2342
        // Note: ideally contacts are only updated if there was a change to:
2343
        //   - The fixtures' sensor states.
2344
        //   - The fixtures bodies' transformations.
2345
        //   - The "maxCirclesRatio" per-step configuration state if contact IS NOT for sensor.
2346
        //   - The "maxDistanceIters" per-step configuration state if contact IS for sensor.
2347
        //
2348
        if (NeedsUpdating(contact)) {
6,752,793✔
2349
            // The following may call listener but is otherwise thread-safe.
2350
#if defined(DO_THREADED)
2351
            contactsNeedingUpdate.push_back(contactID);
2352
            //futures.push_back(async(&Update, this, *contact, conf)));
2353
            //futures.push_back(async(launch::async, [=]{ Update(*contact, conf); }));
2354
#else
2355
            Update(contactID, updateConf);
5,090,000✔
2356
#endif
2357
                ++updated;
5,090,000✔
2358
        }
2359
        else {
2360
            ++skipped;
1,662,793✔
2361
        }
2362
    });
2363
    
2364
#if defined(DO_THREADED)
2365
    auto numJobs = size(contactsNeedingUpdate);
2366
    const auto jobsPerCore = numJobs / 4;
2367
    for (auto i = decltype(numJobs){0}; numJobs > 0 && i < 3; ++i) {
2368
        futures.push_back(std::async(std::launch::async, [=]{
2369
            const auto offset = jobsPerCore * i;
2370
            for (auto j = decltype(jobsPerCore){0}; j < jobsPerCore; ++j) {
2371
                    Update(contactsNeedingUpdate[offset + j], updateConf);
2372
            }
2373
        }));
2374
        numJobs -= jobsPerCore;
2375
    }
2376
    if (numJobs > 0) {
2377
        futures.push_back(std::async(std::launch::async, [=]{
2378
            const auto offset = jobsPerCore * 3;
2379
            for (auto j = decltype(numJobs){0}; j < numJobs; ++j) {
2380
                Update(contactsNeedingUpdate[offset + j], updateConf);
2381
            }
2382
        }));
2383
    }
2384
    for (auto&& future: futures) {
2385
        future.get();
2386
    }
2387
#endif
2388
    
2389
    return UpdateContactsStats{
2390
        static_cast<ContactCounter>(ignored),
2391
        static_cast<ContactCounter>(updated),
2392
        static_cast<ContactCounter>(skipped)
2393
    };
222,557✔
2394
}
2395

2396
ContactCounter
2397
AabbTreeWorld::AddContacts( // NOLINT(readability-function-cognitive-complexity)
484,983✔
2398
                           std::vector<ProxyKey, pmr::polymorphic_allocator<ProxyKey>>&& keys)
2399
{
2400
    const auto numContactsBefore = size(m_contacts);
484,983✔
2401
    for_each(cbegin(keys), cend(keys), [this](const ProxyKey& key) {
484,983✔
2402
        const auto& minKeyLeafData = std::get<1>(key);
113,612✔
2403
        const auto& maxKeyLeafData = std::get<2>(key);
113,612✔
2404
        const auto bodyIdA = minKeyLeafData.bodyId;
113,612✔
2405
        const auto shapeIdA = minKeyLeafData.shapeId;
113,612✔
2406
        const auto bodyIdB = maxKeyLeafData.bodyId;
113,612✔
2407
        const auto shapeIdB = maxKeyLeafData.shapeId;
113,612✔
2408
        assert(bodyIdA != bodyIdB);
113,612✔
2409
        auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
113,612✔
2410
        auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
113,612✔
2411
        const auto& shapeA = m_shapeBuffer[to_underlying(shapeIdA)];
113,612✔
2412
        const auto& shapeB = m_shapeBuffer[to_underlying(shapeIdB)];
113,612✔
2413

2414
        // Does a joint override collision? Is at least one body dynamic?
2415
        if (!EitherIsAccelerable(bodyA, bodyB) ||
113,612✔
2416
            !ShouldCollide(m_jointBuffer, m_bodyJoints, bodyIdA, bodyIdB) ||
227,156✔
2417
            !ShouldCollide(shapeA, shapeB))
113,544✔
2418
        {
2419
            return;
109,836✔
2420
        }
2421

2422
#ifndef NO_RACING
2423
        // Code herein may be racey in a multithreaded context...
2424
        // Would need a lock on bodyA, bodyB, and contacts.
2425
        // A global lock on the world instance should work but then would it have so much
2426
        // contention as to make multi-threaded handling of adding new connections senseless?
2427

2428
        // Have to quickly figure out if there's a contact already added for the current
2429
        // fixture-childindex pair that this method's been called for.
2430
        //
2431
        // In cases where there's a bigger bullet-enabled object that's colliding with lots of
2432
        // smaller objects packed tightly together and overlapping like in the Add Pair Stress
2433
        // Test demo that has some 400 smaller objects, the bigger object could have 387 contacts
2434
        // while the smaller object has 369 or more, and the total world contact count can be over
2435
        // 30,495. While searching linearly through the object with less contacts should help,
2436
        // that may still be a lot of contacts to be going through in the context this function
2437
        // is being called. OTOH, speed seems to be dominated by cache hit-ratio...
2438
        //
2439
        // With compiler optimization enabled and 400 small bodies and Real=double...
2440
        // For world:
2441
        //   World::set<Contact*> shows up as .524 seconds max step
2442
        //   World::list<Contact> shows up as .482 seconds max step.
2443
        // For body:
2444
        //    using contact map w/ proxy ID keys shows up as .561
2445
        // W/ unordered_map: .529 seconds max step (step 15).
2446
        // W/ World::list<Contact> and Body::list<ContactKey,Contact*>   .444s@step15, 1.063s-sumstep20
2447
        // W/ World::list<Contact> and Body::list<ContactKey,Contact*>   .393s@step15, 1.063s-sumstep20
2448
        // W/ World::list<Contact> and Body::list<ContactKey,Contact*>   .412s@step15, 1.012s-sumstep20
2449
        // W/ World::list<Contact> and Body::vector<ContactKey,Contact*> .219s@step15, 0.659s-sumstep20
2450

2451
        // Does a contact already exist?
2452
        // Identify body with least contacts and search it.
2453
        // NOTE: Time trial testing found the following rough ordering of data structures, to be
2454
        // fastest to slowest: vector, list, unorderered_set, unordered_map,
2455
        //     set, map.
2456
        auto& contactsA = m_bodyContacts[to_underlying(bodyIdA)];
113,544✔
2457
        auto& contactsB = m_bodyContacts[to_underlying(bodyIdB)];
113,544✔
2458
        if (FindTypeValue((size(contactsA) < size(contactsB))? contactsA: contactsB, std::get<0>(key))) {
113,544✔
2459
            return;
109,768✔
2460
        }
2461

2462
        if (size(m_contacts) >= MaxContacts) {
3,776✔
2463
            // New contact was needed, but denied due to MaxContacts count being reached.
2464
            return;
×
2465
        }
2466

2467
        const auto contactID = static_cast<ContactID>(static_cast<ContactID::underlying_type>(
7,552✔
2468
            m_contactBuffer.Allocate(minKeyLeafData, maxKeyLeafData)));
3,776✔
2469
        m_islanded.contacts.resize(size(m_contactBuffer));
3,776✔
2470
        m_manifoldBuffer.Allocate();
3,776✔
2471
        auto& contact = m_contactBuffer[to_underlying(contactID)];
3,776✔
2472
        if (IsImpenetrable(bodyA) || IsImpenetrable(bodyB)) {
3,776✔
2473
            SetImpenetrable(contact);
240✔
2474
        }
2475
        if (IsSensor(shapeA) || IsSensor(shapeB)) {
3,776✔
2476
            SetSensor(contact);
4✔
2477
        }
2478
        SetFriction(contact, MixFriction(GetFriction(shapeA), GetFriction(shapeB)));
3,776✔
2479
        SetRestitution(contact, MixRestitution(GetRestitution(shapeA), GetRestitution(shapeB)));
3,776✔
2480

2481
        // Insert into the contacts container.
2482
        //
2483
        // Should the new contact be added at front or back?
2484
        //
2485
        // Original strategy added to the front. Since processing done front to back, front
2486
        // adding means container more a LIFO container, while back adding means more a FIFO.
2487
        //
2488
        m_contacts.emplace_back(std::get<0>(key), contactID);
3,776✔
2489

2490
        // TODO: check contactID unique in contacts containers if !NDEBUG
2491
        contactsA.emplace_back(std::get<0>(key), contactID);
3,776✔
2492
        contactsB.emplace_back(std::get<0>(key), contactID);
3,776✔
2493

2494
        // Wake up the bodies
2495
        if (!IsSensor(contact)) {
3,776✔
2496
            if (IsSpeedable(bodyA)) {
3,772✔
2497
                bodyA.SetAwakeFlag();
3,542✔
2498
            }
2499
            if (IsSpeedable(bodyB)) {
3,772✔
2500
                bodyB.SetAwakeFlag();
3,768✔
2501
            }
2502
        }
2503
#endif
2504
    });
2505
    const auto numContactsAfter = size(m_contacts);
484,983✔
2506
    const auto numContactsAdded = numContactsAfter - numContactsBefore;
484,983✔
2507
#if DO_SORT_ID_LISTS
2508
    if (numContactsAdded > 0u) {
2509
        sort(begin(m_contacts), end(m_contacts), [](const KeyedContactID& a, const KeyedContactID& b){
2510
            return std::get<ContactID>(a) < std::get<ContactID>(b);
2511
        });
2512
    }
2513
#endif
2514
    return static_cast<ContactCounter>(numContactsAdded);
484,983✔
2515
}
2516

2517
const std::vector<DynamicTree::Size>& GetProxies(const AabbTreeWorld& world, BodyID id)
52✔
2518
{
2519
    return At(world.m_bodyProxies, id, noSuchBodyMsg);
52✔
2520
}
2521

2522
const BodyContactIDs& GetContacts(const AabbTreeWorld& world, BodyID id)
10✔
2523
{
2524
    return At(world.m_bodyContacts, id, noSuchBodyMsg);
10✔
2525
}
2526

2527
const BodyJointIDs& GetJoints(const AabbTreeWorld& world, BodyID id)
2✔
2528
{
2529
    return At(world.m_bodyJoints, id, noSuchBodyMsg);
2✔
2530
}
2531

2532
ContactCounter AabbTreeWorld::Synchronize(const ProxyIDs& bodyProxies,
3,067,194✔
2533
                                      const Transformation& xfm0, const Transformation& xfm1,
2534
                                      Real multiplier, Length extension)
2535
{
2536
    auto updatedCount = ContactCounter{0};
3,067,194✔
2537
    assert(::playrho::IsValid(xfm0));
3,067,194✔
2538
    assert(::playrho::IsValid(xfm1));
3,067,194✔
2539
    const auto displacement = multiplier * (xfm1.p - xfm0.p);
3,067,194✔
2540
    for (auto&& e: bodyProxies) {
6,133,762✔
2541
        const auto& node = m_tree.GetNode(e);
3,066,568✔
2542
        const auto leafData = node.AsLeaf();
3,066,568✔
2543
        const auto aabb = ComputeAABB(GetChild(m_shapeBuffer[to_underlying(leafData.shapeId)],
3,066,568✔
2544
                                               leafData.childId), xfm0, xfm1);
3,066,568✔
2545
        if (!Contains(node.GetAABB(), aabb)) {
3,066,568✔
2546
            const auto newAabb = GetDisplacedAABB(GetFattenedAABB(aabb, extension),
65,980✔
2547
                                                  displacement);
2548
            m_tree.UpdateLeaf(e, newAabb);
65,980✔
2549
            m_proxiesForContacts.push_back(e);
65,980✔
2550
            ++updatedCount;
65,980✔
2551
        }
2552
    }
2553
    return updatedCount;
3,067,194✔
2554
}
2555

2556
void AabbTreeWorld::Update( // NOLINT(readability-function-cognitive-complexity)
5,174,396✔
2557
                       ContactID contactID, const ContactUpdateConf& conf)
2558
{
2559
    auto& c = m_contactBuffer[to_underlying(contactID)];
5,174,396✔
2560
    auto& manifold = m_manifoldBuffer[to_underlying(contactID)];
5,174,396✔
2561
    const auto oldManifold = manifold;
5,174,396✔
2562

2563
    // Note: do not assume the fixture AABBs are overlapping or are valid.
2564
    const auto oldTouching = c.IsTouching();
5,174,396✔
2565
    auto newTouching = false;
5,174,396✔
2566

2567
    const auto bodyIdA = GetBodyA(c);
5,174,396✔
2568
    const auto shapeIdA = GetShapeA(c);
5,174,396✔
2569
    const auto indexA = GetChildIndexA(c);
5,174,396✔
2570
    const auto bodyIdB = GetBodyB(c);
5,174,396✔
2571
    const auto shapeIdB = GetShapeB(c);
5,174,396✔
2572
    const auto indexB = GetChildIndexB(c);
5,174,396✔
2573
    const auto& shapeA = m_shapeBuffer[to_underlying(shapeIdA)];
5,174,396✔
2574
    const auto& shapeB = m_shapeBuffer[to_underlying(shapeIdB)];
5,174,396✔
2575
    const auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
5,174,396✔
2576
    const auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
5,174,396✔
2577
    const auto xfA = GetTransformation(bodyA);
5,174,396✔
2578
    const auto xfB = GetTransformation(bodyB);
5,174,396✔
2579
    const auto childA = GetChild(shapeA, indexA);
5,174,396✔
2580
    const auto childB = GetChild(shapeB, indexB);
5,174,396✔
2581

2582
    // NOTE: Ideally, the touching state returned by the TestOverlap function
2583
    //   agrees 100% of the time with that returned from the CollideShapes function.
2584
    //   This is not always the case however especially as the separation or overlap
2585
    //   approaches zero.
2586
#define OVERLAP_TOLERANCE (SquareMeter / Real(20))
2587

2588
    const auto sensor = c.IsSensor();
5,174,396✔
2589
    if (sensor) {
5,174,396✔
2590
        const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
4✔
2591
        newTouching = (overlapping >= 0_m2);
4✔
2592
#ifdef OVERLAP_TOLERANCE
2593
#ifndef NDEBUG
2594
        const auto tolerance = OVERLAP_TOLERANCE;
4✔
2595
        const auto newManifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
4✔
2596
        assert(newTouching == (newManifold.GetPointCount() > 0) ||
4✔
2597
               abs(overlapping) < tolerance);
2598
#endif
2599
#endif
2600
        // Sensors don't generate manifolds.
2601
        manifold = Manifold{};
4✔
2602
    }
2603
    else {
2604
        auto newManifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
5,174,392✔
2605
        const auto old_point_count = oldManifold.GetPointCount();
5,174,392✔
2606
        const auto new_point_count = newManifold.GetPointCount();
5,174,392✔
2607
        newTouching = new_point_count > 0;
5,174,392✔
2608
#ifdef OVERLAP_TOLERANCE
2609
#ifndef NDEBUG
2610
        const auto tolerance = OVERLAP_TOLERANCE;
5,174,392✔
2611
        const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
5,174,392✔
2612
        assert(newTouching == (overlapping >= 0_m2) || abs(overlapping) < tolerance);
5,174,392✔
2613
#endif
2614
#endif
2615
        // Match old contact ids to new contact ids and copy the stored impulses to warm
2616
        // start the solver. Note: missing any opportunities to warm start the solver
2617
        // results in squishier stacking and less stable simulations.
2618
        bool found[2] = {false, new_point_count < 2};
5,174,392✔
2619
        for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i) {
14,701,090✔
2620
            const auto new_cf = newManifold.GetContactFeature(i);
9,526,698✔
2621
            for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j) {
14,224,439✔
2622
                if (new_cf == oldManifold.GetContactFeature(j)) {
14,211,027✔
2623
                    found[i] = true;
9,513,286✔
2624
                    newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
9,513,286✔
2625
                    break;
9,513,286✔
2626
                }
2627
            }
2628
        }
2629
        // If warm starting data wasn't found for a manifold point via contact feature
2630
        // matching, it's better to just set the data to whatever old point is closest
2631
        // to the new one.
2632
        for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i) {
14,701,090✔
2633
            if (!found[i]) {
9,526,698✔
2634
                auto leastSquareDiff = std::numeric_limits<Area>::infinity();
13,412✔
2635
                const auto newPt = newManifold.GetPoint(i);
13,412✔
2636
                for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j) {
15,869✔
2637
                    const auto oldPt = oldManifold.GetPoint(j);
2,457✔
2638
                    const auto squareDiff = GetMagnitudeSquared(oldPt.localPoint - newPt.localPoint);
2,457✔
2639
                    if (leastSquareDiff > squareDiff) {
2,457✔
2640
                        leastSquareDiff = squareDiff;
2,447✔
2641
                        newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
2,447✔
2642
                    }
2643
                }
2644
            }
2645
        }
2646

2647
        // Ideally this function is **NEVER** called unless a dependency changed such
2648
        // that the following assertion is **ALWAYS** valid.
2649
        //assert(newManifold != oldManifold);
2650

2651
        manifold = newManifold;
5,174,392✔
2652

2653
#ifdef MAKE_CONTACT_PROCESSING_ORDER_DEPENDENT
2654
        /*
2655
         * The following code creates an ordering dependency in terms of update processing
2656
         * over a container of contacts. It also puts this function into the situation of
2657
         * modifying bodies which adds race potential in a multi-threaded mode of operation.
2658
         * Lastly, without this code, the step-statistics show a world getting to sleep in
2659
         * less TOI position iterations.
2660
         */
2661
        if (newTouching != oldTouching) {
2662
            bodyA.SetAwake();
2663
            bodyB.SetAwake();
2664
        }
2665
#endif
2666
    }
2667

2668
    c.UnflagForUpdating();
5,174,396✔
2669

2670
    if (!oldTouching && newTouching) {
5,174,396✔
2671
        c.SetTouching();
8,188✔
2672
        if (m_listeners.beginContact) {
8,188✔
2673
            m_listeners.beginContact(contactID);
3,339✔
2674
        }
2675
    }
2676
    else if (oldTouching && !newTouching) {
5,166,208✔
2677
        c.UnsetTouching();
5,437✔
2678
        if (m_listeners.endContact) {
5,437✔
2679
            m_listeners.endContact(contactID);
3,331✔
2680
        }
2681
    }
2682

2683
    if (!sensor && newTouching) {
5,174,396✔
2684
        if (m_listeners.preSolveContact) {
4,827,171✔
2685
            m_listeners.preSolveContact(contactID, oldManifold);
3,689✔
2686
        }
2687
    }
2688
}
5,174,396✔
2689

2690
void SetBody(AabbTreeWorld& world, BodyID id, Body value)
237,823✔
2691
{
2692
    if (IsLocked(world)) {
237,823✔
2693
        throw WrongState(worldIsLockedMsg);
13,356✔
2694
    }
2695
    // Validate id and all the new body's shapeIds...
2696
    auto& body = At(world.m_bodyBuffer, id, noSuchBodyMsg);
224,467✔
2697
    Validate(world.m_shapeBuffer, Span<const ShapeID>(value.GetShapes()), noSuchShapeMsg);
224,463✔
2698
    if (world.m_bodyBuffer.FindFree(to_underlying(id))) {
224,461✔
2699
        throw InvalidArgument(idIsDestroyedMsg);
2✔
2700
    }
2701

2702
    auto addToBodiesForSync = false;
224,459✔
2703
    // handle state changes that other data needs to stay in sync with
2704
    if (GetType(body) != GetType(value)) {
224,459✔
2705
        // Destroy the attached contacts.
2706
        Erase(world.m_bodyContacts[to_underlying(id)], [&world,&body](ContactID contactID) {
18✔
2707
            world.Destroy(contactID, &body);
×
2708
            return true;
×
2709
        });
2710
        switch (value.GetType()) {
18✔
2711
        case BodyType::Static: {
8✔
2712
#ifndef NDEBUG
2713
            const auto xfm1 = GetTransform0(value.GetSweep());
8✔
2714
            const auto xfm2 = GetTransformation(value);
8✔
2715
            assert(xfm1 == xfm2);
8✔
2716
#endif
2717
            addToBodiesForSync = true;
8✔
2718
            break;
8✔
2719
        }
2720
        case BodyType::Kinematic:
10✔
2721
        case BodyType::Dynamic:
2722
            break;
10✔
2723
        }
2724
    }
2725
    const auto shapeIds = GetOldAndNewShapeIDs(body, value);
224,459✔
2726
    if (!empty(shapeIds.first)) {
224,459✔
2727
        auto& bodyProxies = world.m_bodyProxies[to_underlying(id)];
30✔
2728
        const auto lastProxy = end(bodyProxies);
30✔
2729
        bodyProxies.erase(std::remove_if(begin(bodyProxies), lastProxy,
30✔
2730
                                         [&world,&shapeIds](DynamicTree::Size idx){
16✔
2731
            if (Find(shapeIds.first, world.m_tree.GetLeafData(idx).shapeId)) {
4✔
2732
                world.m_tree.DestroyLeaf(idx);
4✔
2733
                EraseFirst(world.m_proxiesForContacts, idx);
4✔
2734
                return true;
4✔
2735
            }
2736
            return false;
×
2737
        }), lastProxy);
2738
    }
2739
    for (auto&& shapeId: shapeIds.first) {
224,489✔
2740
        // Destroy any contacts associated with the fixture.
2741
        Erase(world.m_bodyContacts[to_underlying(id)], [&world,id,shapeId,&body](ContactID contactID) {
30✔
2742
            if (!IsFor(world.m_contactBuffer[to_underlying(contactID)], id, shapeId)) {
×
2743
                return false;
×
2744
            }
2745
            world.Destroy(contactID, &body);
×
2746
            return true;
×
2747
        });
2748
        EraseAll(world.m_fixturesForProxies, std::make_pair(id, shapeId));
30✔
2749
        DestroyProxies(world.m_tree, id, shapeId, world.m_proxiesForContacts);
30✔
2750
    }
2751
    Append(world.m_fixturesForProxies, id, shapeIds.second);
224,459✔
2752
    if (GetTransformation(body) != GetTransformation(value)) {
224,459✔
2753
        FlagForUpdating(world.m_contactBuffer, world.m_bodyContacts[to_underlying(id)]);
22✔
2754
        addToBodiesForSync = true;
22✔
2755
    }
2756
    if (addToBodiesForSync) {
224,459✔
2757
        world.m_bodiesForSync.push_back(id);
30✔
2758
    }
2759
    body = std::move(value);
224,459✔
2760
}
224,459✔
2761

2762
void SetContact(AabbTreeWorld& world, ContactID id, Contact value)
56✔
2763
{
2764
    // Make sure body identifiers and shape identifiers are valid...
2765
    const auto bodyIdA = GetBodyA(value);
56✔
2766
    const auto bodyIdB = GetBodyB(value);
56✔
2767
    GetBody(world, bodyIdA);
56✔
2768
    GetBody(world, bodyIdB);
50✔
2769
    GetChild(GetShape(world, GetShapeA(value)), GetChildIndexA(value));
50✔
2770
    GetChild(GetShape(world, GetShapeB(value)), GetChildIndexB(value));
46✔
2771
    if (world.m_contactBuffer.FindFree(to_underlying(id))) {
46✔
2772
        throw InvalidArgument(idIsDestroyedMsg);
2✔
2773
    }
2774
    auto &contact = At(world.m_contactBuffer, id, noSuchContactMsg);
44✔
2775
    if (contact.GetContactableA() != value.GetContactableA()) {
42✔
2776
        throw InvalidArgument("cannot change contactable A");
2✔
2777
    }
2778
    if (contact.GetContactableB() != value.GetContactableB()) {
40✔
2779
        throw InvalidArgument("cannot change contactable B");
2✔
2780
    }
2781
    if (IsImpenetrable(contact) != IsImpenetrable(value)) {
38✔
2782
        throw InvalidArgument("change body A or B being impenetrable to change impenetrable state");
4✔
2783
    }
2784
    if (IsSensor(contact) != IsSensor(value)) {
34✔
2785
        throw InvalidArgument("change shape A or B being a sensor to change sensor state");
4✔
2786
    }
2787
    if (GetToi(contact) != GetToi(value)) {
30✔
2788
        throw InvalidArgument("user may not change the TOI");
2✔
2789
    }
2790
    if (GetToiCount(contact) != GetToiCount(value)) {
28✔
2791
        throw InvalidArgument("user may not change the TOI count");
2✔
2792
    }
2793
    contact = value;
26✔
2794
}
26✔
2795

2796
const Body& GetBody(const AabbTreeWorld& world, BodyID id)
7,358,969✔
2797
{
2798
    return At(world.m_bodyBuffer, id, noSuchBodyMsg);
7,358,969✔
2799
}
2800

2801
const Joint& GetJoint(const AabbTreeWorld& world, JointID id)
212,628✔
2802
{
2803
    return At(world.m_jointBuffer, id, noSuchJointMsg);
212,628✔
2804
}
2805

2806
const Contact& GetContact(const AabbTreeWorld& world, ContactID id)
445,971✔
2807
{
2808
    return At(world.m_contactBuffer, id, noSuchContactMsg);
445,971✔
2809
}
2810

2811
const Manifold& GetManifold(const AabbTreeWorld& world, ContactID id)
371✔
2812
{
2813
    return At(world.m_manifoldBuffer, id, noSuchManifoldMsg);
371✔
2814
}
2815

2816
ContactID GetSoonestContact(const Span<const KeyedContactID>& ids,
262,230✔
2817
                            const Span<const Contact>& contacts) noexcept
2818
{
2819
    auto found = InvalidContactID;
262,230✔
2820
    auto minToi = UnitIntervalFF<Real>{Real(1)};
262,230✔
2821
    for (const auto& id: ids)
57,868,405✔
2822
    {
2823
        const auto contactID = std::get<ContactID>(id);
57,606,175✔
2824
        assert(to_underlying(contactID) < contacts.size());
57,606,175✔
2825
        const auto& c = contacts[to_underlying(contactID)];
57,606,175✔
2826
        if (const auto toi = c.GetToi())
57,606,175✔
2827
        {
2828
            if (minToi > *toi) {
4,726,215✔
2829
                minToi = *toi;
149,761✔
2830
                found = contactID;
149,761✔
2831
            }
2832
        }
2833
    }
2834
    return found;
262,230✔
2835
}
2836

2837
} // namespace playrho::d2
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc