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

louis-langholtz / PlayRho / 7342124999

27 Dec 2023 09:45PM UTC coverage: 99.303%. Remained the same
7342124999

push

github

louis-langholtz
Address clang-tidy copy warning

11403 of 11483 relevant lines covered (99.3%)

19581144.92 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,572,671✔
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,572,671✔
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,
612,746✔
175
                               const Span<BodyConstraint>& constraints,
176
                               Time h)
177
{
178
    assert(IsValid(h));
612,746✔
179
    for_each(cbegin(bodies), cend(bodies), [&](const auto& id) {
612,746✔
180
        auto& bc = constraints[to_underlying(id)];
4,558,309✔
181
        const auto velocity = bc.GetVelocity();
4,558,309✔
182
        const auto translation = h * velocity.linear;
4,558,309✔
183
        const auto rotation = h * velocity.angular;
4,558,309✔
184
        bc.SetPosition(bc.GetPosition() + Position{translation, rotation});
4,558,309✔
185
    });
4,558,309✔
186
}
612,746✔
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)
7,135,649✔
207
{
208
    assert(var.GetPointCount() >= vc.GetPointCount());
7,135,649✔
209
    
210
    auto assignProc = [&](VelocityConstraint::size_type i) {
11,953,352✔
211
        const auto& point = vc.GetPointAt(i);
11,953,352✔
212
        var.SetPointImpulses(i, point.normalImpulse, point.tangentImpulse);
11,953,352✔
213
    };
19,089,001✔
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();
7,135,649✔
220
    for (auto i = decltype(count){0}; i < count; ++i)
19,089,001✔
221
    {
222
        assignProc(i);
11,953,352✔
223
    }
224
#endif
225
}
7,135,649✔
226

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

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

239
    const auto invMassA = bodyA.GetInvMass();
7,135,649✔
240
    const auto invRotInertiaA = bodyA.GetInvRotInertia();
7,135,649✔
241

242
    const auto invMassB = bodyB.GetInvMass();
7,135,649✔
243
    const auto invRotInertiaB = bodyB.GetInvRotInertia();
7,135,649✔
244

245
    for (auto j = decltype(pointCount){0}; j < pointCount; ++j) {
19,089,001✔
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);
11,953,352✔
252
        const auto P = vcp.normalImpulse * normal + vcp.tangentImpulse * tangent;
11,953,352✔
253
        const auto LA = Cross(vcp.relA, P) / Radian;
11,953,352✔
254
        const auto LB = Cross(vcp.relB, P) / Radian;
11,953,352✔
255
        std::get<0>(vp) -= Velocity{invMassA * P, invRotInertiaA * LA};
11,953,352✔
256
        std::get<1>(vp) += Velocity{invMassB * P, invRotInertiaB * LB};
11,953,352✔
257
    }
258

259
    return vp;
7,135,649✔
260
}
261

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

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

287
PositionConstraints GetPositionConstraints(pmr::memory_resource& resource,
612,746✔
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};
612,746✔
294
    constraints.reserve(size(contacts));
612,746✔
295
    transform(cbegin(contacts), cend(contacts), back_inserter(constraints),
612,746✔
296
              [&](const ContactID& contactID) {
7,393,192✔
297
        const auto& contact = contactBuffer[to_underlying(contactID)];
7,393,192✔
298
        const auto shapeA = GetShapeA(contact);
7,393,192✔
299
        const auto shapeB = GetShapeB(contact);
7,393,192✔
300
        const auto indexA = GetChildIndexA(contact);
7,393,192✔
301
        const auto indexB = GetChildIndexB(contact);
7,393,192✔
302
        const auto bodyA = GetBodyA(contact);
7,393,192✔
303
        const auto bodyB = GetBodyB(contact);
7,393,192✔
304
        const auto radiusA = GetVertexRadius(shapeBuffer[to_underlying(shapeA)], indexA);
7,393,192✔
305
        const auto radiusB = GetVertexRadius(shapeBuffer[to_underlying(shapeB)], indexB);
7,393,192✔
306
        const auto& manifold = manifoldBuffer[to_underlying(contactID)];
7,393,192✔
307
        return PositionConstraint{manifold, bodyA, bodyB, radiusA + radiusB};
7,393,192✔
308
    });
309
    return constraints;
612,746✔
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,
612,746✔
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};
612,746✔
328
    velConstraints.reserve(size(contacts));
612,746✔
329
    transform(cbegin(contacts), cend(contacts), back_inserter(velConstraints),
612,746✔
330
              [&](const auto& contactID) {
7,393,192✔
331
        const auto& contact = contactBuffer[to_underlying(contactID)];
7,393,192✔
332
        const auto bodyA = GetBodyA(contact);
7,393,192✔
333
        const auto bodyB = GetBodyB(contact);
7,393,192✔
334
        const auto shapeIdA = GetShapeA(contact);
7,393,192✔
335
        const auto shapeIdB = GetShapeB(contact);
7,393,192✔
336
        const auto indexA = GetChildIndexA(contact);
7,393,192✔
337
        const auto indexB = GetChildIndexB(contact);
7,393,192✔
338
        const auto friction = GetFriction(contact);
7,393,192✔
339
        const auto restitution = GetRestitution(contact);
7,393,192✔
340
        const auto tangentSpeed = GetTangentSpeed(contact);
7,393,192✔
341
        const auto& bodyConstraintA = bodies[to_underlying(bodyA)];
7,393,192✔
342
        const auto& bodyConstraintB = bodies[to_underlying(bodyB)];
7,393,192✔
343
        const auto radiusA = GetVertexRadius(shapeBuffer[to_underlying(shapeIdA)], indexA);
7,393,192✔
344
        const auto radiusB = GetVertexRadius(shapeBuffer[to_underlying(shapeIdB)], indexB);
7,393,192✔
345
        const auto xfA = GetTransformation(bodyConstraintA.GetPosition(),
7,393,192✔
346
                                           bodyConstraintA.GetLocalCenter());
7,393,192✔
347
        const auto xfB = GetTransformation(bodyConstraintB.GetPosition(),
7,393,192✔
348
                                           bodyConstraintB.GetLocalCenter());
7,393,192✔
349
        const auto& manifold = manifoldBuffer[to_underlying(contactID)];
7,393,192✔
350
        return VelocityConstraint{friction, restitution, tangentSpeed,
351
            GetWorldManifold(manifold, xfA, radiusA, xfB, radiusB),
14,786,384✔
352
            bodyA, bodyB, bodies, conf};
22,179,576✔
353
    });
354
    return velConstraints;
612,746✔
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,
997,249✔
363
                                       const Span<BodyConstraint>& bodies)
364
{
365
    auto maxIncImpulse = 0_Ns;
997,249✔
366
    for_each(begin(velConstraints), end(velConstraints), [&](VelocityConstraint& vc) {
997,249✔
367
        maxIncImpulse = std::max(maxIncImpulse, GaussSeidel::SolveVelocityConstraint(vc, bodies));
56,430,033✔
368
    });
56,430,033✔
369
    return maxIncImpulse;
997,249✔
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,
785,746✔
379
                                     const Span<BodyConstraint>& bodies,
380
                                     const ConstraintSolverConf& conf)
381
{
382
    auto minSeparation = std::numeric_limits<Length>::infinity();
785,746✔
383
    for_each(begin(posConstraints), end(posConstraints), [&](const PositionConstraint &pc) {
785,746✔
384
        assert(pc.bodyA != pc.bodyB); // Confirms ContactManager::Add() did its job.
21,451,193✔
385
        const auto res = GaussSeidel::SolvePositionConstraint(pc, true, true, bodies, conf);
21,451,193✔
386
        bodies[to_underlying(pc.bodyA)].SetPosition(res.pos_a);
21,451,193✔
387
        bodies[to_underlying(pc.bodyB)].SetPosition(res.pos_b);
21,451,193✔
388
        minSeparation = std::min(minSeparation, res.min_separation);
21,451,193✔
389
    });
21,451,193✔
390
    return minSeparation;
785,746✔
391
}
392

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

401
inline Time UpdateUnderActiveTimes(const Span<const BodyID>& bodies,
484,278✔
402
                                   ObjectPool<Body>& bodyBuffer,
403
                                   const StepConf& conf)
404
{
405
    auto minUnderActiveTime = std::numeric_limits<Time>::infinity();
484,278✔
406
    for_each(cbegin(bodies), cend(bodies), [&](const auto& bodyID) {
484,278✔
407
        auto& b = bodyBuffer[to_underlying(bodyID)];
4,301,373✔
408
        if (IsSpeedable(b)) {
4,301,373✔
409
            const auto underActiveTime = GetUnderActiveTime(b, conf);
4,287,493✔
410
            b.SetUnderActiveTime(underActiveTime);
4,287,493✔
411
            minUnderActiveTime = std::min(minUnderActiveTime, underActiveTime);
4,287,493✔
412
        }
413
    });
4,301,373✔
414
    return minUnderActiveTime;
484,278✔
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,469,399✔
430
{
431
    return state == ToiOutput::e_touching;
2,469,399✔
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
1,071,521✔
453
{
454
    return AabbTreeWorld::ContactUpdateConf{GetDistanceConf(conf), GetManifoldConf(conf)};
1,071,521✔
455
}
456

457
template <typename T>
458
void FlagForUpdating(ObjectPool<Contact>& contactsBuffer, const T& contacts) noexcept
3,926,757✔
459
{
460
    std::for_each(begin(contacts), end(contacts), [&](const auto& ci) {
3,926,757✔
461
        contactsBuffer[to_underlying(std::get<ContactID>(ci))].FlagForUpdating();
20,949,216✔
462
    });
463
}
3,926,757✔
464

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

470
bool ShouldCollide(const ObjectPool<Joint>& jointBuffer,
120,779✔
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)];
120,779✔
476
    const auto it = std::find_if(cbegin(joints), cend(joints), [&](const auto& ji) {
120,779✔
477
        return (std::get<BodyID>(ji) == rhs) &&
202✔
478
        !GetCollideConnected(jointBuffer[to_underlying(std::get<JointID>(ji))]);
202✔
479
    });
480
    return it == end(joints);
120,779✔
481
}
482

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

490
void Unset(std::vector<bool>& islanded, const Span<const std::pair<ContactKey, ContactID>>& elements)
228,553✔
491
{
492
    for (const auto& element: elements) {
9,430,650✔
493
        islanded[to_underlying(std::get<ContactID>(element))] = false;
9,202,097✔
494
    }
495
}
228,553✔
496

497
void Unset(std::vector<bool>& islanded, const Span<const std::tuple<ContactKey, ContactID>>& elements)
128,494✔
498
{
499
    for (const auto& element: elements) {
644,151✔
500
        islanded[to_underlying(std::get<ContactID>(element))] = false;
515,657✔
501
    }
502
}
128,494✔
503

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

512
/// @brief Reset contacts for solve TOI.
513
void ResetBodyContactsForSolveTOI(ObjectPool<Contact>& buffer,
128,494✔
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) {
128,494✔
518
        SetToi(buffer[to_underlying(std::get<ContactID>(ci))], {});
515,657✔
519
    });
515,657✔
520
}
128,494✔
521

522
/// @brief Reset contacts for solve TOI.
523
void ResetContactsForSolveTOI(ObjectPool<Contact>& buffer,
228,553✔
524
                              const KeyedContactIDs& contacts) noexcept
525
{
526
    for_each(begin(contacts), end(contacts), [&buffer](const auto& c) {
228,553✔
527
        auto& contact = buffer[to_underlying(std::get<ContactID>(c))];
9,202,097✔
528
        SetToi(contact, {});
9,202,097✔
529
        SetToiCount(contact, 0);
9,202,097✔
530
    });
9,202,097✔
531
}
228,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,
29,230✔
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);
29,230✔
556
    fixtureProxies.reserve(size(fixtureProxies) + childCount);
29,230✔
557
    otherProxies.reserve(size(otherProxies) + childCount);
29,230✔
558
    for (auto childIndex = decltype(childCount){0}; childIndex < childCount; ++childIndex) {
58,470✔
559
        const auto dp = GetChild(shape, childIndex);
29,240✔
560
        const auto aabb = playrho::d2::ComputeAABB(dp, xfm);
29,240✔
561
        const auto fattenedAABB = GetFattenedAABB(aabb, aabbExtension);
29,240✔
562
        const auto treeId = tree.CreateLeaf(fattenedAABB, Contactable{
29,240✔
563
            bodyID, shapeID, childIndex});
29,240✔
564
        fixtureProxies.push_back(treeId);
29,240✔
565
        otherProxies.push_back(treeId);
29,240✔
566
    }
567
    return childCount;
29,230✔
568
}
569

570
template <typename Element, typename Value>
571
auto FindTypeValue(const std::vector<Element>& container, const Value& value)
538,603✔
572
{
573
    const auto last = end(container);
538,603✔
574
    auto it = std::find_if(begin(container), last, [value](const auto& elem) {
1,184,812✔
575
        return std::get<Value>(elem) == value;
646,209✔
576
    });
577
    return (it != last)? std::optional<decltype(it)>{it}: std::optional<decltype(it)>{};
1,077,206✔
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,467✔
637
{
638
    if (IsEnabled(oldBody) && IsEnabled(newBody)) {
224,467✔
639
        auto oldShapeIds = std::vector<ShapeID>{};
224,439✔
640
        auto newShapeIds = std::vector<ShapeID>{};
224,439✔
641
        auto oldmap = std::map<ShapeID, int>{};
224,439✔
642
        auto newmap = std::map<ShapeID, int>{};
224,439✔
643
        for (auto&& i: oldBody.GetShapes()) {
75,255,650✔
644
            ++oldmap[i];
75,031,211✔
645
            --newmap[i];
75,031,211✔
646
        }
647
        for (auto&& i: newBody.GetShapes()) {
75,295,984✔
648
            --oldmap[i];
75,071,545✔
649
            ++newmap[i];
75,071,545✔
650
        }
651
        for (auto&& entry: oldmap) {
300,968✔
652
            for (auto i = 0; i < entry.second; ++i) {
76,551✔
653
                oldShapeIds.push_back(entry.first);
22✔
654
            }
655
        }
656
        for (auto&& entry: newmap) {
300,968✔
657
            for (auto i = 0; i < entry.second; ++i) {
116,885✔
658
                newShapeIds.push_back(entry.first);
40,356✔
659
            }
660
        }
661
        return {oldShapeIds, newShapeIds};
224,439✔
662
    }
224,439✔
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)
685,659✔
674
{
675
    std::fill(begin(vector),
1,371,318✔
676
              begin(vector) + ToSigned(std::min(size(vector), newSize)),
1,371,318✔
677
              newValue);
678
    vector.resize(newSize);
685,659✔
679
}
685,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,
612,746✔
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};
612,746✔
689
    for_each(begin(bodies), end(bodies), [&](BodyID id) {
612,746✔
690
        if (!IsSpeedable(buffer[to_underlying(id)])) {
4,558,309✔
691
            islanded[to_underlying(id)] = false;
142,322✔
692
            ++numRemoved;
142,322✔
693
        }
694
    });
4,558,309✔
695
    return numRemoved;
612,746✔
696
}
697

698
auto FindContacts(pmr::memory_resource& resource,
585,790✔
699
                  const DynamicTree& tree,
700
                  const 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};
585,790✔
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));
585,790✔
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) {
585,790✔
713
        const auto leaf0 = tree.GetLeafData(pid);
100,880✔
714
        const auto aabb = tree.GetAABB(pid);
100,880✔
715
        Query(tree, aabb, [pid,leaf0,&proxyKeys,&tree](DynamicTree::Size nodeId) {
100,880✔
716
            const auto leaf1 = tree.GetLeafData(nodeId);
300,001✔
717
            // A proxy cannot form a pair with itself.
718
            if ((nodeId != pid) && (leaf0.bodyId != leaf1.bodyId)) {
300,001✔
719
                const auto key = ContactKey{pid, nodeId};
146,491✔
720
                if (key.GetMin() == pid) {
146,491✔
721
                    proxyKeys.emplace_back(key, leaf0, leaf1);
67,240✔
722
                }
723
                else {
724
                    proxyKeys.emplace_back(key, leaf1, leaf0);
79,251✔
725
                }
726
            }
727
            return DynamicTreeOpcode::Continue;
300,001✔
728
        });
729
    });
100,880✔
730

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

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

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

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

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

760
auto GetProxyKeysOpts(const WorldConf& conf) -> pmr::PoolMemoryOptions
566✔
761
{
762
    return {conf.reserveBuffers, conf.reserveContactKeys * sizeof(AabbTreeWorld::ProxyKey)};
566✔
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,
378,043✔
783
            BodyID bodyId, Span<const ShapeID> shapeIds) -> void
784
{
785
    for (const auto shapeId: shapeIds) {
427,389✔
786
        fixtures.emplace_back(bodyId, shapeId);
49,346✔
787
    }
788
}
378,043✔
789

790
template <class Container, class ElementType, class Message>
791
auto Validate(const Container& container, const Span<const ElementType>& ids, Message &&msg)
378,051✔
792
-> decltype(container.at(to_underlying(ElementType{})), std::declval<void>())
793
{
794
    for (const auto& id: ids) {
75,458,594✔
795
        At(container, id, std::forward<Message>(msg));
75,080,547✔
796
    }
797
}
378,047✔
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):
566✔
808
    m_statsResource(conf.doStats? conf.upstream: nullptr),
566✔
809
    m_bodyStackResource(GetBodyStackOpts(conf),
566✔
810
                        conf.doStats? &m_statsResource: conf.upstream),
566✔
811
    m_bodyConstraintsResource(GetBodyConstraintOpts(conf),
566✔
812
                              conf.doStats? &m_statsResource: conf.upstream),
566✔
813
    m_positionConstraintsResource(GetPositionConstraintsOpts(conf),
566✔
814
                                  conf.doStats? &m_statsResource: conf.upstream),
566✔
815
    m_velocityConstraintsResource(GetVelocityConstraintsOpts(conf),
566✔
816
                                  conf.doStats? &m_statsResource: conf.upstream),
566✔
817
    m_proxyKeysResource(GetProxyKeysOpts(conf), conf.doStats? &m_statsResource: conf.upstream),
566✔
818
    m_islandResource({conf.reserveBuffers}, conf.doStats? &m_statsResource: conf.upstream),
566✔
819
    m_tree(conf.treeCapacity),
566✔
820
    m_vertexRadius{conf.vertexRadius}
566✔
821
{
822
    m_proxiesForContacts.reserve(conf.proxyCapacity);
566✔
823
    m_contactBuffer.reserve(conf.contactCapacity);
566✔
824
    m_manifoldBuffer.reserve(conf.contactCapacity);
566✔
825
    m_contacts.reserve(conf.contactCapacity);
566✔
826
    m_islanded.contacts.reserve(conf.contactCapacity);
566✔
827
}
566✔
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:
462✔
870
    m_statsResource(other.m_statsResource.upstream_resource()),
462✔
871
    m_bodyConstraintsResource(other.m_bodyConstraintsResource.GetOptions(),
922✔
872
                              other.m_statsResource.upstream_resource()?
462✔
873
                              &m_statsResource: other.m_bodyConstraintsResource.GetUpstream()),
460✔
874
    m_positionConstraintsResource(other.m_positionConstraintsResource.GetOptions(),
922✔
875
                                  other.m_statsResource.upstream_resource()?
462✔
876
                                  &m_statsResource: other.m_positionConstraintsResource.GetUpstream()),
460✔
877
    m_velocityConstraintsResource(other.m_velocityConstraintsResource.GetOptions(),
922✔
878
                                  other.m_statsResource.upstream_resource()?
462✔
879
                                  &m_statsResource: other.m_velocityConstraintsResource.GetUpstream()),
460✔
880
    m_proxyKeysResource(other.m_proxyKeysResource.GetOptions(),
922✔
881
                        other.m_statsResource.upstream_resource()?
462✔
882
                        &m_statsResource: other.m_proxyKeysResource.GetUpstream()),
460✔
883
    m_islandResource(other.m_islandResource.GetOptions(),
922✔
884
                     other.m_statsResource.upstream_resource()?
462✔
885
                     &m_statsResource: other.m_islandResource.GetUpstream()),
460✔
886
    m_tree(std::move(other.m_tree)),
462✔
887
    m_bodyBuffer(std::move(other.m_bodyBuffer)),
462✔
888
    m_shapeBuffer(std::move(other.m_shapeBuffer)),
462✔
889
    m_jointBuffer(std::move(other.m_jointBuffer)),
462✔
890
    m_contactBuffer(std::move(other.m_contactBuffer)),
462✔
891
    m_manifoldBuffer(std::move(other.m_manifoldBuffer)),
462✔
892
    m_bodyContacts(std::move(other.m_bodyContacts)),
462✔
893
    m_bodyJoints(std::move(other.m_bodyJoints)),
462✔
894
    m_bodyProxies(std::move(other.m_bodyProxies)),
462✔
895
    m_proxiesForContacts(std::move(other.m_proxiesForContacts)),
462✔
896
    m_fixturesForProxies(std::move(other.m_fixturesForProxies)),
462✔
897
    m_bodiesForSync(std::move(other.m_bodiesForSync)),
462✔
898
    m_bodies(std::move(other.m_bodies)),
462✔
899
    m_joints(std::move(other.m_joints)),
462✔
900
    m_contacts(std::move(other.m_contacts)),
462✔
901
    m_islanded(std::move(other.m_islanded)),
462✔
902
    m_listeners(std::move(other.m_listeners)),
462✔
903
    m_flags(other.m_flags),
462✔
904
    m_inv_dt0(other.m_inv_dt0),
462✔
905
    m_vertexRadius(other.m_vertexRadius)
462✔
906
{
907
}
462✔
908

909
AabbTreeWorld::~AabbTreeWorld() noexcept
1,040✔
910
{
911
    Clear(*this);
1,040✔
912
}
1,040✔
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,514✔
945
{
946
    if (const auto listener = world.m_listeners.jointDestruction) {
1,514✔
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,514✔
957
    if (const auto listener = world.m_listeners.shapeDestruction) {
1,514✔
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,514✔
972
    world.m_inv_dt0 = 0_Hz;
1,514✔
973
    world.m_flags = AabbTreeWorld::e_stepComplete;
1,514✔
974
    world.m_islanded.bodies.clear();
1,514✔
975
    world.m_islanded.joints.clear();
1,514✔
976
    world.m_islanded.contacts.clear();
1,514✔
977
    world.m_contacts.clear();
1,514✔
978
    world.m_joints.clear();
1,514✔
979
    world.m_bodies.clear();
1,514✔
980
    world.m_bodiesForSync.clear();
1,514✔
981
    world.m_fixturesForProxies.clear();
1,514✔
982
    world.m_proxiesForContacts.clear();
1,514✔
983
    world.m_tree.Clear();
1,514✔
984
    world.m_manifoldBuffer.clear();
1,514✔
985
    world.m_contactBuffer.clear();
1,514✔
986
    world.m_jointBuffer.clear();
1,514✔
987
    world.m_bodyBuffer.clear();
1,514✔
988
    world.m_shapeBuffer.clear();
1,514✔
989
    world.m_bodyProxies.clear();
1,514✔
990
    world.m_bodyContacts.clear();
1,514✔
991
    world.m_bodyJoints.clear();
1,514✔
992
}
1,514✔
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
20✔
1005
{
1006
    return static_cast<ContactCounter>(world.m_contactBuffer.size());
20✔
1007
}
1008

1009
BodyID CreateBody(AabbTreeWorld& world, Body body)
156,921✔
1010
{
1011
    if (IsLocked(world)) {
156,921✔
1012
        throw WrongState(worldIsLockedMsg);
3,339✔
1013
    }
1014
    if (size(world.m_bodies) >= MaxBodies) {
153,582✔
1015
        throw LengthError("CreateBody: operation would exceed MaxBodies");
2✔
1016
    }
1017
    Validate(world.m_shapeBuffer, Span<const ShapeID>(body.GetShapes()), noSuchShapeMsg);
153,580✔
1018
    const auto id = static_cast<BodyID>(
1019
        static_cast<BodyID::underlying_type>(world.m_bodyBuffer.Allocate(std::move(body))));
153,578✔
1020
    world.m_islanded.bodies.resize(size(world.m_bodyBuffer));
153,578✔
1021
    const auto bodyContactsIndex = world.m_bodyContacts.Allocate();
153,578✔
1022
    static constexpr auto DefaultBodyContactsReserveSize = 32u;
1023
    world.m_bodyContacts[bodyContactsIndex].reserve(DefaultBodyContactsReserveSize);
153,578✔
1024
    world.m_bodyJoints.Allocate();
153,578✔
1025
    const auto bodyProxiesIndex = world.m_bodyProxies.Allocate();
153,578✔
1026
    world.m_bodyProxies[bodyProxiesIndex].reserve(1u);
153,578✔
1027
    world.m_bodies.push_back(id);
153,578✔
1028
    const auto& bufferedBody = world.m_bodyBuffer[to_underlying(id)];
153,578✔
1029
    if (IsEnabled(bufferedBody)) {
153,578✔
1030
        Append(world.m_fixturesForProxies, id, bufferedBody.GetShapes());
153,576✔
1031
    }
1032
    return id;
153,578✔
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)
141,945✔
1228
{
1229
    const auto vertexRadius = GetVertexRadiusInterval(world);
141,945✔
1230
    const auto childCount = GetChildCount(def);
141,945✔
1231
    for (auto i = ChildCounter{0}; i < childCount; ++i) {
283,892✔
1232
        const auto vr = GetVertexRadius(def, i);
141,951✔
1233
        if (!(vr >= vertexRadius.GetMin())) {
141,951✔
1234
            throw InvalidArgument("CreateShape: vertex radius < min");
2✔
1235
        }
1236
        if (!(vr <= vertexRadius.GetMax())) {
141,949✔
1237
            throw InvalidArgument("CreateShape: vertex radius > max");
2✔
1238
        }
1239
    }
1240
    if (IsLocked(world)) {
141,941✔
1241
        throw WrongState(worldIsLockedMsg);
3,339✔
1242
    }
1243
    if (size(world.m_shapeBuffer) >= MaxShapes) {
138,602✔
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))));
138,600✔
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,016✔
1275
{
1276
    return At(world.m_shapeBuffer, id, noSuchShapeMsg);
25,041,016✔
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,
484,278✔
1351
                            BodyCounter& remNumBodies,
1352
                            ContactCounter& remNumContacts,
1353
                            JointCounter& remNumJoints)
1354
{
1355
#ifndef NDEBUG
1356
    assert(!m_islanded.bodies[to_underlying(seedID)]);
484,278✔
1357
    auto& seed = m_bodyBuffer[to_underlying(seedID)];
484,278✔
1358
    assert(IsSpeedable(seed));
484,278✔
1359
    assert(IsAwake(seed));
484,278✔
1360
    assert(IsEnabled(seed));
484,278✔
1361
    assert(remNumBodies != 0);
484,278✔
1362
    assert(remNumBodies < MaxBodies);
484,278✔
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};
484,278✔
1367
    stack.reserve(remNumBodies);
484,278✔
1368
    stack.push_back(seedID);
484,278✔
1369
    m_islanded.bodies[to_underlying(seedID)] = true;
484,278✔
1370
    AddToIsland(island, stack, remNumBodies, remNumContacts, remNumJoints);
484,278✔
1371
}
484,278✔
1372

1373
void AabbTreeWorld::AddToIsland(Island& island, BodyStack& stack,
484,278✔
1374
                            BodyCounter& remNumBodies,
1375
                            ContactCounter& remNumContacts,
1376
                            JointCounter& remNumJoints)
1377
{
1378
    while (!empty(stack)) {
4,785,651✔
1379
        // Grab the next body off the stack and add it to the island.
1380
        const auto bodyID = stack.back();
4,301,373✔
1381
        stack.pop_back();
4,301,373✔
1382

1383
        auto& body = m_bodyBuffer[to_underlying(bodyID)];
4,301,373✔
1384

1385
        assert(IsEnabled(body));
4,301,373✔
1386
        island.bodies.push_back(bodyID);
4,301,373✔
1387
        assert(remNumBodies > 0);
4,301,373✔
1388
        --remNumBodies;
4,301,373✔
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)) {
4,301,373✔
1393
            continue;
13,880✔
1394
        }
1395

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

1399
        const auto oldNumContacts = size(island.contacts);
4,287,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);
4,287,493✔
1402

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

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

1413
        remNumJoints -= static_cast<JointCounter>(size(island.joints) - numJoints);
4,287,493✔
1414
    }
1415
}
484,278✔
1416

1417
void AabbTreeWorld::AddContactsToIsland(Island& island, BodyStack& stack,
4,287,493✔
1418
                                    const BodyContactIDs& contacts,
1419
                                    BodyID bodyID)
1420
{
1421
    for_each(cbegin(contacts), cend(contacts), [&](const auto& ci) {
4,287,493✔
1422
        const auto contactID = std::get<ContactID>(ci);
16,400,018✔
1423
        if (!m_islanded.contacts[to_underlying(contactID)]) {
16,400,018✔
1424
            const auto& contact = m_contactBuffer[to_underlying(contactID)];
9,614,473✔
1425
            if (IsEnabled(contact) && IsTouching(contact) && !IsSensor(contact)) {
9,614,473✔
1426
                const auto other = GetOtherBody(contact, bodyID);
7,135,649✔
1427
                island.contacts.push_back(contactID);
7,135,649✔
1428
                m_islanded.contacts[to_underlying(contactID)] = true;
7,135,649✔
1429
                if (!m_islanded.bodies[to_underlying(other)]) {
7,135,649✔
1430
                    m_islanded.bodies[to_underlying(other)] = true;
3,814,938✔
1431
                    stack.push_back(other);
3,814,938✔
1432
                }
1433
            }
1434
        }
1435
    });
16,400,018✔
1436
}
4,287,493✔
1437

1438
void AabbTreeWorld::AddJointsToIsland(Island& island, BodyStack& stack,
4,287,493✔
1439
                                  const BodyJointIDs& joints)
1440
{
1441
    for_each(cbegin(joints), cend(joints), [this,&island,&stack](const auto& ji) {
4,287,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
}
4,287,493✔
1463

1464
RegStepStats AabbTreeWorld::SolveReg(const StepConf& conf)
228,553✔
1465
{
1466
    auto stats = RegStepStats{};
228,553✔
1467
    auto remNumBodies = static_cast<BodyCounter>(size(m_bodies)); // Remaining # of bodies.
228,553✔
1468
    auto remNumContacts = static_cast<ContactCounter>(size(m_contacts)); // Remaining # of contacts.
228,553✔
1469
    auto remNumJoints = static_cast<JointCounter>(size(m_joints)); // Remaining # of joints.
228,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);
228,553✔
1476
    ResizeAndReset(m_islanded.contacts, size(m_contactBuffer), false);
228,553✔
1477
    ResizeAndReset(m_islanded.joints, size(m_jointBuffer), false);
228,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) {
5,575,230✔
1485
        if (!m_islanded.bodies[to_underlying(b)]) {
5,346,677✔
1486
            auto& body = m_bodyBuffer[to_underlying(b)];
1,543,464✔
1487
            assert(!IsAwake(body) || IsSpeedable(body));
1,543,464✔
1488
            if (IsAwake(body) && IsEnabled(body)) {
1,543,464✔
1489
                ++stats.islandsFound;
484,278✔
1490
                Island island{m_islandResource, m_islandResource, m_islandResource};
484,278✔
1491
                // Size the island for the remaining un-evaluated contacts.
1492
                island.bodies.reserve(remNumBodies);
484,278✔
1493
                island.contacts.reserve(remNumContacts);
484,278✔
1494
                island.joints.reserve(remNumJoints);
484,278✔
1495
                AddToIsland(island, b, remNumBodies, remNumContacts, remNumJoints);
484,278✔
1496
#if defined(DO_SORT_ISLANDS)
1497
                Sort(island);
1498
#endif
1499
                stats.maxIslandBodies = std::max(stats.maxIslandBodies,
968,556✔
1500
                                                 static_cast<BodyCounter>(size(island.bodies)));
484,278✔
1501
                const auto numRemoved = RemoveUnspeedablesFromIslanded(island.bodies, m_bodyBuffer, m_islanded.bodies);
484,278✔
1502
                remNumBodies += static_cast<BodyCounter>(numRemoved);
484,278✔
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
                ::playrho::Update(stats, SolveRegIslandViaGS(conf, island));
484,278✔
1509
#endif
1510
            }
484,278✔
1511
        }
1512
    }
1513

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

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

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

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

1550
    // Update bodies' pos0 values.
1551
    for_each(cbegin(island.bodies), cend(island.bodies), [&](const auto& bodyID) {
484,278✔
1552
        auto& body = m_bodyBuffer[to_underlying(bodyID)];
4,301,373✔
1553
        SetPosition0(body, GetPosition1(body));
4,301,373✔
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
    });
4,301,373✔
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));
484,278✔
1562
    auto posConstraints = GetPositionConstraints(m_positionConstraintsResource, island.contacts,
484,278✔
1563
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer);
484,278✔
1564
    auto velConstraints = GetVelocityConstraints(m_velocityConstraintsResource, island.contacts,
484,278✔
1565
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer,
484,278✔
1566
                                                 bodyConstraints,
1567
                                                 GetRegVelocityConstraintConf(conf));
484,278✔
1568
    if (conf.doWarmStart) {
484,278✔
1569
        WarmStartVelocities(velConstraints, bodyConstraints);
484,264✔
1570
    }
1571

1572
    const auto psConf = GetRegConstraintSolverConf(conf);
484,278✔
1573

1574
    for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
484,278✔
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;
484,278✔
1580
    for (auto i = decltype(conf.regVelocityIters){0}; i < conf.regVelocityIters; ++i) {
601,900✔
1581
        auto jointsOkay = true;
586,062✔
1582
        for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
586,062✔
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);
586,062✔
1589
        results.maxIncImpulse = std::max(results.maxIncImpulse, newIncImpulse);
586,062✔
1590
        if (jointsOkay && (newIncImpulse <= conf.regMinMomentum)) {
586,062✔
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;
468,440✔
1597
            break;
468,440✔
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);
484,278✔
1603
    
1604
    // Solve position constraints
1605
    for (auto i = decltype(conf.regPositionIters){0}; i < conf.regPositionIters; ++i) {
526,160✔
1606
        const auto minSeparation = SolvePositionConstraintsViaGS(posConstraints, bodyConstraints,
511,245✔
1607
                                                                 psConf);
511,245✔
1608
        results.minSeparation = std::min(results.minSeparation, minSeparation);
511,245✔
1609
        const auto contactsOkay = (minSeparation >= conf.regMinSeparation);
511,245✔
1610
        auto jointsOkay = true;
511,245✔
1611
        for_each(cbegin(island.joints), cend(island.joints), [&](const auto& id) {
511,245✔
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) {
511,245✔
1616
            // Reached tolerance, early out...
1617
            results.positionIters = i + 1;
469,363✔
1618
            results.solved = true;
469,363✔
1619
            break;
469,363✔
1620
        }
1621
    }
1622
    
1623
    // Update normal and tangent impulses of contacts' manifold points
1624
    for_each(cbegin(velConstraints), cend(velConstraints), [&](const VelocityConstraint& vc) {
484,278✔
1625
        const auto i = static_cast<VelocityConstraints::size_type>(&vc - data(velConstraints));
7,135,649✔
1626
        AssignImpulses(m_manifoldBuffer[to_underlying(island.contacts[i])], vc);
7,135,649✔
1627
    });
7,135,649✔
1628

1629
    for (const auto& id: island.bodies) {
4,785,651✔
1630
        const auto i = to_underlying(id);
4,301,373✔
1631
        const auto& bc = bodyConstraints[i];
4,301,373✔
1632
        auto& body = m_bodyBuffer[i];
4,301,373✔
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());
4,301,373✔
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) {
4,301,373✔
1638
            SetPosition1(body, pos);
3,537,987✔
1639
            FlagForUpdating(m_contactBuffer, m_bodyContacts[i]);
3,537,987✔
1640
        }
1641
    }
1642

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

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

1655
    return results;
968,556✔
1656
}
484,278✔
1657

1658
AabbTreeWorld::UpdateContactsData
1659
AabbTreeWorld::UpdateContactTOIs(const StepConf& conf)
357,021✔
1660
{
1661
    auto results = UpdateContactsData{};
357,021✔
1662

1663
    const auto toiConf = GetToiConf(conf);
357,021✔
1664
    for (const auto& contact: m_contacts)
96,850,422✔
1665
    {
1666
        auto& c = m_contactBuffer[to_underlying(std::get<ContactID>(contact))];
96,493,401✔
1667
        if (HasValidToi(c))
96,493,401✔
1668
        {
1669
            ++results.numValidTOI;
6,332,301✔
1670
            continue;
94,024,002✔
1671
        }
1672
        if (!IsEnabled(c) || IsSensor(c) || !IsImpenetrable(c))
90,161,100✔
1673
        {
1674
            continue;
87,691,168✔
1675
        }
1676
        if (GetToiCount(c) >= conf.maxSubSteps)
2,469,932✔
1677
        {
1678
            // What are the pros/cons of this?
1679
            // Larger m_maxSubSteps slows down the simulation.
1680
            // m_maxSubSteps of 44 and higher seems to decrease the occurrance of tunneling
1681
            // of multiple bullet body collisions with static objects.
1682
            ++results.numAtMaxSubSteps;
3✔
1683
            continue;
3✔
1684
        }
1685

1686
        auto& bA = m_bodyBuffer[to_underlying(GetBodyA(c))];
2,469,929✔
1687
        auto& bB = m_bodyBuffer[to_underlying(GetBodyB(c))];
2,469,929✔
1688

1689
        if (!IsAwake(bA) && !IsAwake(bB)) {
2,469,929✔
1690
            continue;
530✔
1691
        }
1692

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

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

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

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

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

1727
    return results;
357,021✔
1728
}
1729

1730
ToiStepStats AabbTreeWorld::SolveToi(const StepConf& conf)
228,557✔
1731
{
1732
    auto stats = ToiStepStats{};
228,557✔
1733

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

1741
    const auto subStepping = GetSubStepping(*this);
228,557✔
1742

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

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

1776
        // Reset island flags and synchronize broad-phase proxies.
1777
        for (const auto& bodyId: m_bodies) {
42,900,168✔
1778
            if (m_islanded.bodies[to_underlying(bodyId)]) {
42,771,700✔
1779
                m_islanded.bodies[to_underlying(bodyId)] = false;
128,494✔
1780
                const auto& body = m_bodyBuffer[to_underlying(bodyId)];
128,494✔
1781
                if (IsAccelerable(body)) {
128,494✔
1782
                    stats.proxiesMoved += Synchronize(m_bodyProxies[to_underlying(bodyId)],
128,494✔
1783
                                                      GetTransform0(body.GetSweep()),
128,494✔
1784
                                                      GetTransformation(body),
1785
                                                      conf.displaceMultiplier, conf.aabbExtension);
128,494✔
1786
                    const auto& bodyContacts = m_bodyContacts[to_underlying(bodyId)];
128,494✔
1787
                    ResetBodyContactsForSolveTOI(m_contactBuffer, bodyContacts);
128,494✔
1788
                    Unset(m_islanded.contacts, bodyContacts);
128,494✔
1789
                }
1790
            }
1791
        }
1792

1793
        // Commit fixture proxy movements to the broad-phase so that new contacts are created.
1794
        // Also, some contacts can be destroyed.
1795
        stats.contactsAdded += AddContacts(
128,468✔
1796
            FindContacts(m_proxyKeysResource, m_tree, std::exchange(m_proxiesForContacts, {})),
256,936✔
1797
            conf);
1798

1799
        if (subStepping) {
128,468✔
1800
            m_flags &= ~e_stepComplete;
4✔
1801
            break;
4✔
1802
        }
1803
    }
128,464✔
1804
    return stats;
228,557✔
1805
}
1806

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

1815
    auto numUpdated = ContactCounter{0};
128,468✔
1816
    auto numSkipped = ContactCounter{0};
128,468✔
1817
    auto& contact = m_contactBuffer[to_underlying(contactID)];
128,468✔
1818

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

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

1835
    {
1836
        const auto backupA = GetSweep(bA);
128,468✔
1837
        const auto backupB = GetSweep(bB);
128,468✔
1838

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

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

1852
        SetToi(contact, {});
128,468✔
1853
        contact.IncrementToiCount();
128,468✔
1854

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

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

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

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

1914
#if defined(DO_SORT_ISLANDS)
1915
    Sort(island);
1916
#endif
1917
    RemoveUnspeedablesFromIslanded(island.bodies, m_bodyBuffer, m_islanded.bodies);
128,468✔
1918

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

1925
IslandStats AabbTreeWorld::SolveToiViaGS(const Island& island, const StepConf& conf)
128,468✔
1926
{
1927
    auto results = IslandStats{};
128,468✔
1928

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

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

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

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

1981
    auto velConstraints = GetVelocityConstraints(m_velocityConstraintsResource, island.contacts,
128,468✔
1982
                                                 m_contactBuffer, m_manifoldBuffer, m_shapeBuffer,
128,468✔
1983
                                                 bodyConstraints,
1984
                                                 GetToiVelocityConstraintConf(conf));
128,468✔
1985

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

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

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

2007
    IntegratePositions(island.bodies, bodyConstraints, conf.deltaTime);
128,468✔
2008
    for (const auto& id: island.bodies) {
385,404✔
2009
        const auto i = to_underlying(id);
256,936✔
2010
        auto& body = m_bodyBuffer[i];
256,936✔
2011
        auto& bc = bodyConstraints[i];
256,936✔
2012
        body.JustSetVelocity(bc.GetVelocity());
256,936✔
2013
        if (const auto pos = bc.GetPosition(); GetPosition1(body) != pos) {
256,936✔
2014
            SetPosition1(body, pos);
128,494✔
2015
            FlagForUpdating(m_contactBuffer, m_bodyContacts[i]);
128,494✔
2016
        }
2017
    }
2018

2019
    if (m_listeners.postSolveContact) {
128,468✔
2020
        Report(m_listeners.postSolveContact, island.contacts, velConstraints, results.positionIters);
3,332✔
2021
    }
2022

2023
    return results;
256,936✔
2024
}
128,468✔
2025

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

2033
    assert(m_islanded.bodies[to_underlying(id)]);
128,494✔
2034
    assert(IsAccelerable(body));
128,494✔
2035

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

2042
    // Note: the original contact (for body of which this function was called) already is-in-island.
2043
    const auto bodyImpenetrable = IsImpenetrable(body);
128,494✔
2044
    for (const auto& ci: m_bodyContacts[to_underlying(id)]) {
644,151✔
2045
        const auto contactID = std::get<ContactID>(ci);
515,657✔
2046
        if (!m_islanded.contacts[to_underlying(contactID)]) {
515,657✔
2047
            auto& contact = m_contactBuffer[to_underlying(contactID)];
387,163✔
2048
            if (!IsSensor(contact)) {
387,163✔
2049
                const auto otherId = GetOtherBody(contact, id);
387,163✔
2050
                auto& other = m_bodyBuffer[to_underlying(otherId)];
387,163✔
2051
                if (bodyImpenetrable || IsImpenetrable(other)) {
387,163✔
2052
                    const auto otherIslanded = m_islanded.bodies[to_underlying(otherId)];
137,671✔
2053
                    {
2054
                        const auto backup = GetSweep(other);
137,671✔
2055
                        if (!otherIslanded /* && GetSweep(other).alpha0 != toi */) {
137,671✔
2056
                            Advance(other, toi);
3,302✔
2057
                            FlagForUpdating(m_contactBuffer, m_bodyContacts[to_underlying(otherId)]);
3,302✔
2058
                        }
2059

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

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

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

2114
    if (IsLocked(world)) {
232,108✔
2115
        throw WrongState(worldIsLockedMsg);
3,339✔
2116
    }
2117

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

2123
        // Create proxies herein for access to conf.aabbExtension info!
2124
        for (const auto& [bodyID, shapeID]: world.m_fixturesForProxies) {
257,999✔
2125
            stepStats.pre.proxiesCreated +=
29,230✔
2126
                CreateProxies(world.m_tree, bodyID, shapeID, world.m_shapeBuffer[to_underlying(shapeID)],
29,230✔
2127
                              GetTransformation(world.m_bodyBuffer[to_underlying(bodyID)]),
29,230✔
2128
                              conf.aabbExtension,
29,230✔
2129
                              world.m_bodyProxies[to_underlying(bodyID)], world.m_proxiesForContacts);
29,230✔
2130
        }
2131
        world.m_fixturesForProxies = {};
228,769✔
2132

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

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

2153
        {
2154
            // Could potentially run UpdateContacts multithreaded over split lists...
2155
            const auto updateStats = world.UpdateContacts(conf);
228,769✔
2156
            stepStats.pre.contactsIgnored = updateStats.ignored;
228,769✔
2157
            stepStats.pre.contactsUpdated = updateStats.updated;
228,769✔
2158
            stepStats.pre.contactsSkipped = updateStats.skipped;
228,769✔
2159
        }
2160

2161

2162
        // For any new fixtures added: need to find and create the new contacts.
2163
        // Note: this may update bodies (in addition to the contacts container).
2164
        stepStats.pre.contactsAdded = world.AddContacts(
228,769✔
2165
            FindContacts(world.m_proxyKeysResource, world.m_tree, std::exchange(world.m_proxiesForContacts, {})),
457,538✔
2166
            conf);
2167

2168
        if (conf.deltaTime != 0_s) {
228,769✔
2169
            world.m_inv_dt0 = Real(1) / conf.deltaTime;
228,557✔
2170

2171
            // Integrate velocities, solve velocity constraints, and integrate positions.
2172
            if (IsStepComplete(world)) {
228,557✔
2173
                stepStats.reg = world.SolveReg(conf);
228,553✔
2174
            }
2175

2176
            // Handle TOI events.
2177
            if (conf.doToi) {
228,557✔
2178
                stepStats.toi = world.SolveToi(conf);
228,557✔
2179
            }
2180
        }
2181
    }
228,769✔
2182
    return stepStats;
228,769✔
2183
}
2184

2185
void ShiftOrigin(AabbTreeWorld& world, const Length2& newOrigin)
3,345✔
2186
{
2187
    if (IsLocked(world)) {
3,345✔
2188
        throw WrongState(worldIsLockedMsg);
3,339✔
2189
    }
2190

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

2201
    for_each(begin(world.m_joints), end(world.m_joints), [&](const auto& joint) {
6✔
2202
        auto& j = world.m_jointBuffer[to_underlying(joint)];
2✔
2203
        ::playrho::d2::ShiftOrigin(j, newOrigin);
2✔
2204
    });
2✔
2205

2206
    world.m_tree.ShiftOrigin(newOrigin);
6✔
2207
}
6✔
2208

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

2245
void AabbTreeWorld::Destroy(ContactID contactID, const Body* from)
6✔
2246
{
2247
    assert(contactID != InvalidContactID);
6✔
2248
    if (const auto found = FindTypeValue(m_contacts, contactID)) {
6✔
2249
        m_contacts.erase(*found);
6✔
2250
    }
2251
    InternalDestroy(contactID, from);
6✔
2252
}
6✔
2253

2254
bool IsDestroyed(const AabbTreeWorld& world, ContactID id) noexcept
6✔
2255
{
2256
    return world.m_contactBuffer.FindFree(to_underlying(id));
6✔
2257
}
2258

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

2302
AabbTreeWorld::UpdateContactsStats AabbTreeWorld::UpdateContacts(const StepConf& conf)
228,769✔
2303
{
2304
#ifdef DO_PAR_UNSEQ
2305
    atomic<std::uint32_t> ignored;
2306
    atomic<std::uint32_t> updated;
2307
    atomic<std::uint32_t> skipped;
2308
#else
2309
    auto ignored = std::uint32_t{0};
228,769✔
2310
    auto updated = std::uint32_t{0};
228,769✔
2311
    auto skipped = std::uint32_t{0};
228,769✔
2312
#endif
2313

2314
    const auto updateConf = GetUpdateConf(conf);
228,769✔
2315
    
2316
#if defined(DO_THREADED)
2317
    std::vector<ContactID> contactsNeedingUpdate;
2318
    contactsNeedingUpdate.reserve(size(m_contacts));
2319
    std::vector<std::future<void>> futures;
2320
    futures.reserve(size(m_contacts));
2321
#endif
2322

2323
    // Update awake contacts.
2324
    for_each(/*execution::par_unseq,*/ begin(m_contacts), end(m_contacts), [&](const auto& c) {
228,769✔
2325
        const auto contactID = std::get<ContactID>(c);
9,197,481✔
2326
        auto& contact = m_contactBuffer[to_underlying(contactID)];
9,197,481✔
2327
        const auto& bodyA = m_bodyBuffer[to_underlying(GetBodyA(contact))];
9,197,481✔
2328
        const auto& bodyB = m_bodyBuffer[to_underlying(GetBodyB(contact))];
9,197,481✔
2329

2330
        // Awake && speedable (dynamic or kinematic) means collidable.
2331
        // At least one body must be collidable
2332
        assert(!IsAwake(bodyA) || IsSpeedable(bodyA));
9,197,481✔
2333
        assert(!IsAwake(bodyB) || IsSpeedable(bodyB));
9,197,481✔
2334
        if (!IsAwake(bodyA) && !IsAwake(bodyB)) {
9,197,481✔
2335
            // This sometimes fails... is it important?
2336
            //assert(!contact.HasValidToi());
2337
            ++ignored;
20✔
2338
            return;
20✔
2339
        }
2340

2341
        // Possible that bodyA.GetSweep().alpha0 != 0
2342
        // Possible that bodyB.GetSweep().alpha0 != 0
2343

2344
        SetEnabled(contact);
9,197,461✔
2345

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

2401
ContactCounter
2402
AabbTreeWorld::AddContacts( // NOLINT(readability-function-cognitive-complexity)
585,790✔
2403
    std::vector<ProxyKey, pmr::polymorphic_allocator<ProxyKey>>&& keys,
2404
    const StepConf& conf)
2405
{
2406
    const auto numContactsBefore = size(m_contacts);
585,790✔
2407
    const auto updateConf = GetUpdateConf(conf);
585,790✔
2408
    for_each(cbegin(keys), cend(keys), [this,&updateConf](const ProxyKey& key) {
585,790✔
2409
        const auto& minKeyLeafData = std::get<1>(key);
120,789✔
2410
        const auto& maxKeyLeafData = std::get<2>(key);
120,789✔
2411
        const auto bodyIdA = minKeyLeafData.bodyId;
120,789✔
2412
        const auto shapeIdA = minKeyLeafData.shapeId;
120,789✔
2413
        const auto bodyIdB = maxKeyLeafData.bodyId;
120,789✔
2414
        const auto shapeIdB = maxKeyLeafData.shapeId;
120,789✔
2415
        assert(bodyIdA != bodyIdB);
120,789✔
2416
        auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
120,789✔
2417
        auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
120,789✔
2418
        const auto& shapeA = m_shapeBuffer[to_underlying(shapeIdA)];
120,789✔
2419
        const auto& shapeB = m_shapeBuffer[to_underlying(shapeIdB)];
120,789✔
2420

2421
        // Does a joint override collision? Is at least one body dynamic?
2422
        if (!EitherIsAccelerable(bodyA, bodyB) ||
120,789✔
2423
            !ShouldCollide(m_jointBuffer, m_bodyJoints, bodyIdA, bodyIdB) ||
241,510✔
2424
            !ShouldCollide(shapeA, shapeB))
120,721✔
2425
        {
2426
            return;
116,157✔
2427
        }
2428

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

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

2458
        // Does a contact already exist?
2459
        // Identify body with least contacts and search it.
2460
        // NOTE: Time trial testing found the following rough ordering of data structures, to be
2461
        // fastest to slowest: vector, list, unorderered_set, unordered_map,
2462
        //     set, map.
2463
        auto& contactsA = m_bodyContacts[to_underlying(bodyIdA)];
120,721✔
2464
        auto& contactsB = m_bodyContacts[to_underlying(bodyIdB)];
120,721✔
2465
        if (FindTypeValue((size(contactsA) < size(contactsB))? contactsA: contactsB, std::get<0>(key))) {
120,721✔
2466
            return;
116,089✔
2467
        }
2468

2469
        if (size(m_contacts) >= MaxContacts) {
4,632✔
2470
            // New contact was needed, but denied due to MaxContacts count being reached.
2471
            return;
×
2472
        }
2473

2474
        const auto contactID = static_cast<ContactID>(static_cast<ContactID::underlying_type>(
9,264✔
2475
            m_contactBuffer.Allocate(minKeyLeafData, maxKeyLeafData)));
4,632✔
2476
        m_islanded.contacts.resize(size(m_contactBuffer));
4,632✔
2477
        m_manifoldBuffer.Allocate();
4,632✔
2478
        auto& contact = m_contactBuffer[to_underlying(contactID)];
4,632✔
2479
        if (IsImpenetrable(bodyA) || IsImpenetrable(bodyB)) {
4,632✔
2480
            SetImpenetrable(contact);
326✔
2481
        }
2482
        if (IsSensor(shapeA) || IsSensor(shapeB)) {
4,632✔
2483
            SetSensor(contact);
4✔
2484
        }
2485
        SetFriction(contact, MixFriction(GetFriction(shapeA), GetFriction(shapeB)));
4,632✔
2486
        SetRestitution(contact, MixRestitution(GetRestitution(shapeA), GetRestitution(shapeB)));
4,632✔
2487

2488
        // Insert into the contacts container.
2489
        //
2490
        // Should the new contact be added at front or back?
2491
        //
2492
        // Original strategy added to the front. Since processing done front to back, front
2493
        // adding means container more a LIFO container, while back adding means more a FIFO.
2494
        //
2495
        m_contacts.emplace_back(std::get<0>(key), contactID);
4,632✔
2496

2497
        // TODO: check contactID unique in contacts containers if !NDEBUG
2498
        contactsA.emplace_back(std::get<0>(key), contactID);
4,632✔
2499
        contactsB.emplace_back(std::get<0>(key), contactID);
4,632✔
2500

2501
        if (!IsSensor(contact)) {
4,632✔
2502
            if (IsSpeedable(bodyA)) {
4,628✔
2503
                bodyA.SetAwakeFlag();
4,312✔
2504
            }
2505
            if (IsSpeedable(bodyB)) {
4,628✔
2506
                bodyB.SetAwakeFlag();
4,624✔
2507
            }
2508
        }
2509

2510
        Update(contactID, updateConf);
4,632✔
2511
#endif
2512
    });
2513
    const auto numContactsAfter = size(m_contacts);
585,790✔
2514
    const auto numContactsAdded = numContactsAfter - numContactsBefore;
585,790✔
2515
#if DO_SORT_ID_LISTS
2516
    if (numContactsAdded > 0u) {
2517
        sort(begin(m_contacts), end(m_contacts), [](const KeyedContactID& a, const KeyedContactID& b){
2518
            return std::get<ContactID>(a) < std::get<ContactID>(b);
2519
        });
2520
    }
2521
#endif
2522
    return static_cast<ContactCounter>(numContactsAdded);
585,790✔
2523
}
2524

2525
const std::vector<DynamicTree::Size>& GetProxies(const AabbTreeWorld& world, BodyID id)
52✔
2526
{
2527
    return At(world.m_bodyProxies, id, noSuchBodyMsg);
52✔
2528
}
2529

2530
const BodyContactIDs& GetContacts(const AabbTreeWorld& world, BodyID id)
10✔
2531
{
2532
    return At(world.m_bodyContacts, id, noSuchBodyMsg);
10✔
2533
}
2534

2535
const BodyJointIDs& GetJoints(const AabbTreeWorld& world, BodyID id)
2✔
2536
{
2537
    return At(world.m_bodyJoints, id, noSuchBodyMsg);
2✔
2538
}
2539

2540
ContactCounter AabbTreeWorld::Synchronize(const ProxyIDs& bodyProxies,
4,415,997✔
2541
                                      const Transformation& xfm0, const Transformation& xfm1,
2542
                                      Real multiplier, Length extension)
2543
{
2544
    auto updatedCount = ContactCounter{0};
4,415,997✔
2545
    assert(::playrho::IsValid(xfm0));
4,415,997✔
2546
    assert(::playrho::IsValid(xfm1));
4,415,997✔
2547
    const auto displacement = multiplier * (xfm1.p - xfm0.p);
4,415,997✔
2548
    for (auto&& e: bodyProxies) {
8,831,368✔
2549
        const auto& node = m_tree.GetNode(e);
4,415,371✔
2550
        const auto leafData = node.AsLeaf();
4,415,371✔
2551
        const auto aabb = ComputeAABB(GetChild(m_shapeBuffer[to_underlying(leafData.shapeId)],
4,415,371✔
2552
                                               leafData.childId), xfm0, xfm1);
4,415,371✔
2553
        if (!Contains(node.GetAABB(), aabb)) {
4,415,371✔
2554
            const auto newAabb = GetDisplacedAABB(GetFattenedAABB(aabb, extension),
71,636✔
2555
                                                  displacement);
2556
            m_tree.UpdateLeaf(e, newAabb);
71,636✔
2557
            m_proxiesForContacts.push_back(e);
71,636✔
2558
            ++updatedCount;
71,636✔
2559
        }
2560
    }
2561
    return updatedCount;
4,415,997✔
2562
}
2563

2564
void AabbTreeWorld::Update( // NOLINT(readability-function-cognitive-complexity)
7,248,334✔
2565
    ContactID contactID, const ContactUpdateConf& conf)
2566
{
2567
    auto& c = m_contactBuffer[to_underlying(contactID)];
7,248,334✔
2568
    auto& manifold = m_manifoldBuffer[to_underlying(contactID)];
7,248,334✔
2569
    const auto oldManifold = manifold;
7,248,334✔
2570

2571
    // Note: do not assume the fixture AABBs are overlapping or are valid.
2572
    const auto oldTouching = c.IsTouching();
7,248,334✔
2573
    auto newTouching = false;
7,248,334✔
2574

2575
    const auto bodyIdA = GetBodyA(c);
7,248,334✔
2576
    const auto shapeIdA = GetShapeA(c);
7,248,334✔
2577
    const auto indexA = GetChildIndexA(c);
7,248,334✔
2578
    const auto bodyIdB = GetBodyB(c);
7,248,334✔
2579
    const auto shapeIdB = GetShapeB(c);
7,248,334✔
2580
    const auto indexB = GetChildIndexB(c);
7,248,334✔
2581
    const auto& shapeA = m_shapeBuffer[to_underlying(shapeIdA)];
7,248,334✔
2582
    const auto& shapeB = m_shapeBuffer[to_underlying(shapeIdB)];
7,248,334✔
2583
    const auto& bodyA = m_bodyBuffer[to_underlying(bodyIdA)];
7,248,334✔
2584
    const auto& bodyB = m_bodyBuffer[to_underlying(bodyIdB)];
7,248,334✔
2585
    const auto xfA = GetTransformation(bodyA);
7,248,334✔
2586
    const auto xfB = GetTransformation(bodyB);
7,248,334✔
2587
    const auto childA = GetChild(shapeA, indexA);
7,248,334✔
2588
    const auto childB = GetChild(shapeB, indexB);
7,248,334✔
2589

2590
    // NOTE: Ideally, the touching state returned by the TestOverlap function
2591
    //   agrees 100% of the time with that returned from the CollideShapes function.
2592
    //   This is not always the case however especially as the separation or overlap
2593
    //   approaches zero.
2594
#define OVERLAP_TOLERANCE (SquareMeter / Real(20))
2595

2596
    const auto sensor = c.IsSensor();
7,248,334✔
2597
    if (sensor) {
7,248,334✔
2598
        const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
4✔
2599
        newTouching = (overlapping >= 0_m2);
4✔
2600
#ifdef OVERLAP_TOLERANCE
2601
#ifndef NDEBUG
2602
        const auto tolerance = OVERLAP_TOLERANCE;
4✔
2603
        const auto newManifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
4✔
2604
        assert(newTouching == (newManifold.GetPointCount() > 0) ||
4✔
2605
               abs(overlapping) < tolerance);
2606
#endif
2607
#endif
2608
        // Sensors don't generate manifolds.
2609
        manifold = Manifold{};
4✔
2610
    }
2611
    else {
2612
        auto newManifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
7,248,330✔
2613
        const auto old_point_count = oldManifold.GetPointCount();
7,248,330✔
2614
        const auto new_point_count = newManifold.GetPointCount();
7,248,330✔
2615
        newTouching = new_point_count > 0;
7,248,330✔
2616
#ifdef OVERLAP_TOLERANCE
2617
#ifndef NDEBUG
2618
        const auto tolerance = OVERLAP_TOLERANCE;
7,248,330✔
2619
        const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
7,248,330✔
2620
        assert(newTouching == (overlapping >= 0_m2) || abs(overlapping) < tolerance);
7,248,330✔
2621
#endif
2622
#endif
2623
        // Match old contact ids to new contact ids and copy the stored impulses to warm
2624
        // start the solver. Note: missing any opportunities to warm start the solver
2625
        // results in squishier stacking and less stable simulations.
2626
        bool found[2] = {false, new_point_count < 2};
7,248,330✔
2627
        for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i) {
20,360,148✔
2628
            const auto new_cf = newManifold.GetContactFeature(i);
13,111,818✔
2629
            for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j) {
19,407,942✔
2630
                if (new_cf == oldManifold.GetContactFeature(j)) {
19,049,526✔
2631
                    found[i] = true;
12,753,402✔
2632
                    newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
12,753,402✔
2633
                    break;
12,753,402✔
2634
                }
2635
            }
2636
        }
2637
        // If warm starting data wasn't found for a manifold point via contact feature
2638
        // matching, it's better to just set the data to whatever old point is closest
2639
        // to the new one.
2640
        for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i) {
20,360,148✔
2641
            if (!found[i]) {
13,111,818✔
2642
                auto leastSquareDiff = std::numeric_limits<Area>::infinity();
358,416✔
2643
                const auto newPt = newManifold.GetPoint(i);
358,416✔
2644
                for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j) {
360,877✔
2645
                    const auto oldPt = oldManifold.GetPoint(j);
2,461✔
2646
                    const auto squareDiff = GetMagnitudeSquared(oldPt.localPoint - newPt.localPoint);
2,461✔
2647
                    if (leastSquareDiff > squareDiff) {
2,461✔
2648
                        leastSquareDiff = squareDiff;
2,451✔
2649
                        newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
2,451✔
2650
                    }
2651
                }
2652
            }
2653
        }
2654

2655
        // Ideally this function is **NEVER** called unless a dependency changed such
2656
        // that the following assertion is **ALWAYS** valid.
2657
        //assert(newManifold != oldManifold);
2658

2659
        manifold = newManifold;
7,248,330✔
2660

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

2676
    c.UnflagForUpdating();
7,248,334✔
2677

2678
    if (!oldTouching && newTouching) {
7,248,334✔
2679
        c.SetTouching();
180,884✔
2680
        if (m_listeners.beginContact) {
180,884✔
2681
            m_listeners.beginContact(contactID);
3,339✔
2682
        }
2683
    }
2684
    else if (oldTouching && !newTouching) {
7,067,450✔
2685
        c.UnsetTouching();
177,331✔
2686
        if (m_listeners.endContact) {
177,331✔
2687
            m_listeners.endContact(contactID);
3,331✔
2688
        }
2689
    }
2690

2691
    if (!sensor && newTouching) {
7,248,334✔
2692
        if (m_listeners.preSolveContact) {
6,641,608✔
2693
            m_listeners.preSolveContact(contactID, oldManifold);
3,693✔
2694
        }
2695
    }
2696
}
7,248,334✔
2697

2698
void SetBody(AabbTreeWorld& world, BodyID id, Body value)
237,831✔
2699
{
2700
    if (IsLocked(world)) {
237,831✔
2701
        throw WrongState(worldIsLockedMsg);
13,356✔
2702
    }
2703
    // Validate id and all the new body's shapeIds...
2704
    auto& body = At(world.m_bodyBuffer, id, noSuchBodyMsg);
224,475✔
2705
    Validate(world.m_shapeBuffer, Span<const ShapeID>(value.GetShapes()), noSuchShapeMsg);
224,471✔
2706
    if (world.m_bodyBuffer.FindFree(to_underlying(id))) {
224,469✔
2707
        throw InvalidArgument(idIsDestroyedMsg);
2✔
2708
    }
2709

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

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

2804
const Body& GetBody(const AabbTreeWorld& world, BodyID id)
7,359,407✔
2805
{
2806
    return At(world.m_bodyBuffer, id, noSuchBodyMsg);
7,359,407✔
2807
}
2808

2809
const Joint& GetJoint(const AabbTreeWorld& world, JointID id)
212,628✔
2810
{
2811
    return At(world.m_jointBuffer, id, noSuchJointMsg);
212,628✔
2812
}
2813

2814
const Contact& GetContact(const AabbTreeWorld& world, ContactID id)
445,975✔
2815
{
2816
    return At(world.m_contactBuffer, id, noSuchContactMsg);
445,975✔
2817
}
2818

2819
const Manifold& GetManifold(const AabbTreeWorld& world, ContactID id)
375✔
2820
{
2821
    return At(world.m_manifoldBuffer, id, noSuchManifoldMsg);
375✔
2822
}
2823

2824
ContactID GetSoonestContact(const Span<const KeyedContactID>& ids,
357,033✔
2825
                            const Span<const Contact>& contacts) noexcept
2826
{
2827
    auto found = InvalidContactID;
357,033✔
2828
    auto minToi = UnitIntervalFF<Real>{Real(1)};
357,033✔
2829
    for (const auto& id: ids)
96,850,454✔
2830
    {
2831
        const auto contactID = std::get<ContactID>(id);
96,493,421✔
2832
        assert(to_underlying(contactID) < contacts.size());
96,493,421✔
2833
        const auto& c = contacts[to_underlying(contactID)];
96,493,421✔
2834
        if (const auto toi = c.GetToi())
96,493,421✔
2835
        {
2836
            if (minToi > *toi) {
8,801,712✔
2837
                minToi = *toi;
431,145✔
2838
                found = contactID;
431,145✔
2839
            }
2840
        }
2841
    }
2842
    return found;
357,033✔
2843
}
2844

2845
} // 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