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

Razakhel / RaZ / 17879500877

20 Sep 2025 11:50AM UTC coverage: 74.251% (+0.003%) from 74.248%
17879500877

push

github

Razakhel
[Utils/Shape] Optimized the AABB's point containment check

- The check is now done in the box's local space, granting a speedup of around 30%
  - It's possible to go further by precomputing the centroid & half-extents, giving a speedup of around 64%, but is left for another time as other shapes might benefit from caching data too

- Added a tolerance to handle calculation errors

17 of 25 new or added lines in 3 files covered. (68.0%)

1 existing line in 1 file now uncovered.

8348 of 11243 relevant lines covered (74.25%)

1757.96 hits per line

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

54.27
/src/RaZ/Utils/Shape.cpp
1
#include "RaZ/Utils/Shape.hpp"
2

3
namespace Raz {
4

5
// Line functions
6

7
bool Line::intersects(const Line&) const {
×
8
  throw std::runtime_error("Error: Not implemented yet.");
×
9
}
10

11
bool Line::intersects(const Plane& plane) const {
74✔
12
  const Vec3f lineVec = m_endPos - m_beginPos;
74✔
13
  const float lineVecPlaneAngle = lineVec.dot(plane.getNormal());
74✔
14

15
  // If near 0, the line & the plane are parallel to each other
16
  if (FloatUtils::areNearlyEqual(lineVecPlaneAngle, 0.f))
74✔
17
    return false;
2✔
18

19
  const float lineStartPlaneAngle = m_beginPos.dot(plane.getNormal());
72✔
20

21
  // Calculating the relative distance along the line where it is intersected by the plane
22
  // If this distance is below 0 or above 1, the intersection isn't between the line's two extremities
23
  const float intersectDist = (plane.getDistance() - lineStartPlaneAngle) / lineVecPlaneAngle;
72✔
24
  return ((intersectDist >= 0.f) && (intersectDist <= 1.f));
72✔
25
}
26

27
bool Line::intersects(const Sphere& sphere) const {
×
28
  const Vec3f projPoint = computeProjection(sphere.getCenter());
×
29
  return sphere.contains(projPoint);
×
30
}
31

32
bool Line::intersects(const Triangle&) const {
×
33
  throw std::runtime_error("Error: Not implemented yet.");
×
34
}
35

36
bool Line::intersects(const Quad&) const {
×
37
  throw std::runtime_error("Error: Not implemented yet.");
×
38
}
39

40
bool Line::intersects(const AABB& aabb) const {
13✔
41
  const Ray lineRay(m_beginPos, (m_endPos - m_beginPos).normalize());
13✔
42
  RayHit hit;
13✔
43

44
  if (!lineRay.intersects(aabb, &hit))
13✔
45
    return false;
6✔
46

47
  // Some implementations check for the hit distance to be positive or 0. However, since our ray-AABB intersection check returns true
48
  //  with a negative distance when the ray's origin is inside the box, this check would be meaningless
49
  // Actually, if reaching here, none of the potential cases should require to check that the hit distance is non-negative
50

51
  // In certain cases, it's even harmful to do so. Given a line segment defined by points A & B, one being in a box & the other outside:
52
  //
53
  // ----------
54
  // |        |
55
  // |   A x-----x B
56
  // |        |
57
  // ----------
58
  //
59
  // Depending on the order of the points, the result would not be symmetrical: B->A would return a positive distance, telling there's an
60
  //  intersection, and A->B a negative distance, telling there's none
61

62
  return (hit.distance * hit.distance <= computeSquaredLength());
7✔
63
}
64

65
bool Line::intersects(const OBB&) const {
×
66
  throw std::runtime_error("Error: Not implemented yet.");
×
67
}
68

69
void Line::translate(const Vec3f& displacement) noexcept {
3✔
70
  m_beginPos += displacement;
3✔
71
  m_endPos   += displacement;
3✔
72
}
3✔
73

74
Vec3f Line::computeProjection(const Vec3f& point) const {
19✔
75
  const Vec3f lineVec   = m_endPos - m_beginPos;
19✔
76
  const float pointDist = lineVec.dot(point - m_beginPos) / lineVec.computeSquaredLength();
19✔
77

78
  // Clamping pointDist between 0 & 1, since it can be outside these bounds if not directly projectable
79
  //
80
  //        < 0        |    >= 0 & <= 1    |        > 1
81
  // __________________________________________________________
82
  //                   |                   |
83
  // P                 |         P         |                  P
84
  // |                 |         |         |                  |
85
  // v                 |         v         |                  v
86
  //    A----------B   |   A----------B    |   A----------B
87

88
  return m_beginPos + lineVec * std::clamp(pointDist, 0.f, 1.f);
19✔
89
}
90

91
AABB Line::computeBoundingBox() const {
4✔
92
  const auto [xMin, xMax] = std::minmax(m_beginPos.x(), m_endPos.x());
4✔
93
  const auto [yMin, yMax] = std::minmax(m_beginPos.y(), m_endPos.y());
4✔
94
  const auto [zMin, zMax] = std::minmax(m_beginPos.z(), m_endPos.z());
4✔
95

96
  return AABB(Vec3f(xMin, yMin, zMin), Vec3f(xMax, yMax, zMax));
4✔
97
}
98

99
// Plane functions
100

101
bool Plane::intersects(const Plane& plane) const {
10✔
102
  const float planesAngle = m_normal.dot(plane.getNormal());
10✔
103
  return !FloatUtils::areNearlyEqual(std::abs(planesAngle), 1.f);
10✔
104
}
105

106
bool Plane::intersects(const Sphere& sphere) const {
9✔
107
  const Vec3f projPoint = computeProjection(sphere.getCenter());
9✔
108
  return sphere.contains(projPoint);
9✔
109
}
110

111
bool Plane::intersects(const Triangle&) const {
×
112
  throw std::runtime_error("Error: Not implemented yet.");
×
113
}
114

115
bool Plane::intersects(const Quad&) const {
×
116
  throw std::runtime_error("Error: Not implemented yet.");
×
117
}
118

119
bool Plane::intersects(const AABB& aabb) const {
×
120
  const Vec3f halfExtents = aabb.computeHalfExtents();
×
121

122
  const float topBoxDist = halfExtents.x() * std::abs(m_normal.x())
×
123
                         + halfExtents.y() * std::abs(m_normal.y())
×
124
                         + halfExtents.z() * std::abs(m_normal.z());
×
125
  const float boxDist = m_normal.dot(aabb.computeCentroid()) - m_distance;
×
126

127
  return (std::abs(boxDist) <= topBoxDist);
×
128
}
129

130
bool Plane::intersects(const OBB&) const {
×
131
  throw std::runtime_error("Error: Not implemented yet.");
×
132
}
133

134
AABB Plane::computeBoundingBox() const {
×
135
  throw std::runtime_error("Error: Not implemented yet.");
×
136
}
137

138
// Sphere functions
139

140
bool Sphere::contains(const Vec3f& point) const {
25✔
141
  const float pointSqDist = (m_centerPos - point).computeSquaredLength();
25✔
142
  return (pointSqDist <= (m_radius * m_radius));
25✔
143
}
144

145
bool Sphere::intersects(const Sphere& sphere) const {
13✔
146
  const float sqDist   = (m_centerPos - sphere.getCenter()).computeSquaredLength();
13✔
147
  const float sumRadii = m_radius + sphere.getRadius();
13✔
148

149
  return (sqDist <= sumRadii * sumRadii);
13✔
150
}
151

152
bool Sphere::intersects(const Triangle& triangle) const {
×
153
  const Vec3f projPoint = triangle.computeProjection(m_centerPos);
×
154
  return contains(projPoint);
×
155
}
156

157
bool Sphere::intersects(const Quad& quad) const {
×
158
  const Vec3f projPoint = quad.computeProjection(m_centerPos);
×
159
  return contains(projPoint);
×
160
}
161

162
bool Sphere::intersects(const AABB& aabb) const {
1✔
163
  const Vec3f projPoint = aabb.computeProjection(m_centerPos);
1✔
164
  return contains(projPoint);
2✔
165
}
166

167
bool Sphere::intersects(const OBB&) const {
×
168
  throw std::runtime_error("Error: Not implemented yet.");
×
169
}
170

171
AABB Sphere::computeBoundingBox() const {
5✔
172
  return AABB(m_centerPos - m_radius, m_centerPos + m_radius);
5✔
173
}
174

175
// Triangle functions
176

177
bool Triangle::intersects(const Triangle&) const {
×
178
  throw std::runtime_error("Error: Not implemented yet.");
×
179
}
180

181
bool Triangle::intersects(const Quad&) const {
×
182
  throw std::runtime_error("Error: Not implemented yet.");
×
183
}
184

185
bool Triangle::intersects(const AABB&) const {
×
186
  throw std::runtime_error("Error: Not implemented yet.");
×
187
}
188

189
bool Triangle::intersects(const OBB&) const {
×
190
  throw std::runtime_error("Error: Not implemented yet.");
×
191
}
192

193
void Triangle::translate(const Vec3f& displacement) noexcept {
3✔
194
  m_firstPos  += displacement;
3✔
195
  m_secondPos += displacement;
3✔
196
  m_thirdPos  += displacement;
3✔
197
}
3✔
198

199
Vec3f Triangle::computeProjection(const Vec3f&) const {
5✔
200
  throw std::runtime_error("Error: Not implemented yet.");
5✔
201
}
202

203
AABB Triangle::computeBoundingBox() const {
97✔
204
  const auto [xMin, xMax] = std::minmax({ m_firstPos.x(), m_secondPos.x(), m_thirdPos.x() });
97✔
205
  const auto [yMin, yMax] = std::minmax({ m_firstPos.y(), m_secondPos.y(), m_thirdPos.y() });
97✔
206
  const auto [zMin, zMax] = std::minmax({ m_firstPos.z(), m_secondPos.z(), m_thirdPos.z() });
97✔
207

208
  return AABB(Vec3f(xMin, yMin, zMin), Vec3f(xMax, yMax, zMax));
97✔
209
}
210

211
Vec3f Triangle::computeNormal() const {
20,066✔
212
  const Vec3f firstEdge  = m_secondPos - m_firstPos;
20,066✔
213
  const Vec3f secondEdge = m_thirdPos - m_firstPos;
20,066✔
214

215
  return firstEdge.cross(secondEdge).normalize();
20,066✔
216
}
217

218
void Triangle::makeCounterClockwise(const Vec3f& normal) {
3✔
219
  if (isCounterClockwise(normal))
3✔
220
    return;
1✔
221

222
  // It doesn't matter which ones are swapped, as long as two of them are
223
  // The 3 points being adjacent, the ordering will be reversed all the same
224
  std::swap(m_firstPos, m_secondPos);
2✔
225
}
226

227
// Quad functions
228

229
bool Quad::intersects(const Quad&) const {
×
230
  throw std::runtime_error("Error: Not implemented yet.");
×
231
}
232

233
bool Quad::intersects(const AABB&) const {
×
234
  throw std::runtime_error("Error: Not implemented yet.");
×
235
}
236

237
bool Quad::intersects(const OBB&) const {
×
238
  throw std::runtime_error("Error: Not implemented yet.");
×
239
}
240

NEW
241
void Quad::translate(const Vec3f& displacement) noexcept {
×
NEW
242
  m_leftTopPos     += displacement;
×
NEW
243
  m_rightTopPos    += displacement;
×
NEW
244
  m_rightBottomPos += displacement;
×
NEW
245
  m_leftBottomPos  += displacement;
×
UNCOV
246
}
×
247

248
Vec3f Quad::computeProjection(const Vec3f&) const {
×
249
  throw std::runtime_error("Error: Not implemented yet.");
×
250
}
251

252
AABB Quad::computeBoundingBox() const {
×
253
  const auto [xMin, xMax] = std::minmax({ m_leftTopPos.x(), m_rightTopPos.x(), m_rightBottomPos.x(), m_leftBottomPos.x() });
×
254
  const auto [yMin, yMax] = std::minmax({ m_leftTopPos.y(), m_rightTopPos.y(), m_rightBottomPos.y(), m_leftBottomPos.y() });
×
255
  const auto [zMin, zMax] = std::minmax({ m_leftTopPos.z(), m_rightTopPos.z(), m_rightBottomPos.z(), m_leftBottomPos.z() });
×
256

257
  return AABB(Vec3f(xMin, yMin, zMin), Vec3f(xMax, yMax, zMax));
×
258
}
259

260
// AABB functions
261

262
bool AABB::contains(const Vec3f& point) const {
27✔
263
  const Vec3f localPoint  = point - computeCentroid();
27✔
264
  const Vec3f halfExtents = computeHalfExtents();
27✔
265

266
  return (std::abs(localPoint.x()) <= halfExtents.x() + std::numeric_limits<float>::epsilon()
27✔
267
       && std::abs(localPoint.y()) <= halfExtents.y() + std::numeric_limits<float>::epsilon()
14✔
268
       && std::abs(localPoint.z()) <= halfExtents.z() + std::numeric_limits<float>::epsilon());
41✔
269
}
270

271
bool AABB::intersects(const AABB& aabb) const {
14✔
272
  const Vec3f& minPoint1 = m_minPos;
14✔
273
  const Vec3f& maxPoint1 = m_maxPos;
14✔
274

275
  const Vec3f& minPoint2 = aabb.getMinPosition();
14✔
276
  const Vec3f& maxPoint2 = aabb.getMaxPosition();
14✔
277

278
  // We determine for each axis if there are extremities that are overlapping
279
  // If the max point of one AABB is further on an axis than the min point of the other, they intersect each other on this axis
280
  //
281
  //            max1
282
  //             v
283
  //    ----------
284
  //    |        |
285
  //    |     ----------
286
  //    |     |  |     |
287
  //    ------|---     |
288
  //          |        |
289
  //          ----------
290
  //          ^
291
  //        min2
292

293
  const bool intersectsX = (minPoint1.x() <= maxPoint2.x()) && (maxPoint1.x() >= minPoint2.x());
14✔
294
  const bool intersectsY = (minPoint1.y() <= maxPoint2.y()) && (maxPoint1.y() >= minPoint2.y());
14✔
295
  const bool intersectsZ = (minPoint1.z() <= maxPoint2.z()) && (maxPoint1.z() >= minPoint2.z());
14✔
296

297
  return (intersectsX && intersectsY && intersectsZ);
14✔
298
}
299

300
bool AABB::intersects(const OBB&) const {
×
301
  throw std::runtime_error("Error: Not implemented yet.");
×
302
}
303

304
void AABB::translate(const Vec3f& displacement) noexcept {
3✔
305
  m_minPos += displacement;
3✔
306
  m_maxPos += displacement;
3✔
307
}
3✔
308

309
Vec3f AABB::computeProjection(const Vec3f& point) const {
2✔
310
  const float closestX = std::max(std::min(point.x(), m_maxPos.x()), m_minPos.x());
2✔
311
  const float closestY = std::max(std::min(point.y(), m_maxPos.y()), m_minPos.y());
2✔
312
  const float closestZ = std::max(std::min(point.z(), m_maxPos.z()), m_minPos.z());
2✔
313

314
  return Vec3f(closestX, closestY, closestZ);
2✔
315
}
316

317
// OBB functions
318

319
void OBB::setRotation(const Quaternionf& rotation) {
×
320
  m_rotation    = rotation;
×
321
  m_invRotation = m_rotation.inverse();
×
322
}
×
323

324
bool OBB::contains(const Vec3f&) const {
×
325
  throw std::runtime_error("Error: Not implemented yet.");
×
326
}
327

328
bool OBB::intersects(const OBB&) const {
×
329
  throw std::runtime_error("Error: Not implemented yet.");
×
330
}
331

332
Vec3f OBB::computeProjection(const Vec3f&) const {
×
333
  throw std::runtime_error("Error: Not implemented yet.");
×
334
}
335

336
AABB OBB::computeBoundingBox() const {
×
337
  throw std::runtime_error("Error: Not implemented yet.");
×
338
}
339

340
} // namespace Raz
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

© 2025 Coveralls, Inc