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

google / vector_math.dart / 17181727464

04 Aug 2025 07:19PM UTC coverage: 26.702% (+0.3%) from 26.388%
17181727464

push

github

web-flow
Bump min SDK to 3.7, update dependencies, reformat (#348)

496 of 1182 new or added lines in 55 files covered. (41.96%)

18 existing lines in 8 files now uncovered.

4463 of 16714 relevant lines covered (26.7%)

1.18 hits per line

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

87.61
/lib/src/vector_math/obb3.dart
1
// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
2
// All rights reserved. Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
part of '../../vector_math.dart';
5

6
/// Defines a 3-dimensional oriented bounding box defined with a [center],
7
/// [halfExtents] and axes.
8
class Obb3 {
9
  final Vector3 _center;
10
  final Vector3 _halfExtents;
11
  final Vector3 _axis0;
12
  final Vector3 _axis1;
13
  final Vector3 _axis2;
14

15
  /// The center of the OBB.
16
  Vector3 get center => _center;
2✔
17

18
  /// The half extends of the OBB.
19
  Vector3 get halfExtents => _halfExtents;
2✔
20

21
  /// The first axis of the OBB.
22
  Vector3 get axis0 => _axis0;
2✔
23

24
  /// The second axis of the OBB.
25
  Vector3 get axis1 => _axis1;
2✔
26

27
  /// The third axis of the OBB.
28
  Vector3 get axis2 => _axis2;
2✔
29

30
  /// Create a new OBB with erverything set to zero.
31
  Obb3()
1✔
32
    : _center = Vector3.zero(),
1✔
33
      _halfExtents = Vector3.zero(),
1✔
34
      _axis0 = Vector3(1.0, 0.0, 0.0),
1✔
35
      _axis1 = Vector3(0.0, 1.0, 0.0),
1✔
36
      _axis2 = Vector3(0.0, 0.0, 1.0);
1✔
37

38
  /// Create a new OBB as a copy of [other].
39
  Obb3.copy(Obb3 other)
×
NEW
40
    : _center = Vector3.copy(other._center),
×
NEW
41
      _halfExtents = Vector3.copy(other._halfExtents),
×
NEW
42
      _axis0 = Vector3.copy(other._axis0),
×
NEW
43
      _axis1 = Vector3.copy(other._axis1),
×
NEW
44
      _axis2 = Vector3.copy(other._axis2);
×
45

46
  /// Create a new OBB using [center], [halfExtents] and axis.
47
  Obb3.centerExtentsAxes(
1✔
48
    Vector3 center,
49
    Vector3 halfExtents,
50
    Vector3 axis0,
51
    Vector3 axis1,
52
    Vector3 axis2,
53
  ) : _center = Vector3.copy(center),
1✔
54
      _halfExtents = Vector3.copy(halfExtents),
1✔
55
      _axis0 = Vector3.copy(axis0),
1✔
56
      _axis1 = Vector3.copy(axis1),
1✔
57
      _axis2 = Vector3.copy(axis2);
1✔
58

59
  /// Copy from [other] into this.
60
  void copyFrom(Obb3 other) {
×
61
    _center.setFrom(other._center);
×
62
    _halfExtents.setFrom(other._halfExtents);
×
63
    _axis0.setFrom(other._axis0);
×
64
    _axis1.setFrom(other._axis1);
×
65
    _axis2.setFrom(other._axis2);
×
66
  }
67

68
  /// Copy from this into [other].
69
  void copyInto(Obb3 other) {
×
70
    other._center.setFrom(_center);
×
71
    other._halfExtents.setFrom(_halfExtents);
×
72
    other._axis0.setFrom(_axis0);
×
73
    other._axis1.setFrom(_axis1);
×
74
    other._axis2.setFrom(_axis2);
×
75
  }
76

77
  /// Reset the rotation of this.
78
  void resetRotation() {
×
79
    _axis0.setValues(1.0, 0.0, 0.0);
×
80
    _axis1.setValues(0.0, 1.0, 0.0);
×
81
    _axis2.setValues(0.0, 0.0, 1.0);
×
82
  }
83

84
  /// Translate this by [offset].
85
  void translate(Vector3 offset) {
1✔
86
    _center.add(offset);
2✔
87
  }
88

89
  /// Rotate this by the rotation matrix [t].
90
  void rotate(Matrix3 t) {
1✔
91
    t
92
      ..transform(_axis0..scale(_halfExtents.x))
5✔
93
      ..transform(_axis1..scale(_halfExtents.y))
5✔
94
      ..transform(_axis2..scale(_halfExtents.z));
5✔
95

96
    _halfExtents
1✔
97
      ..x = _axis0.normalize()
3✔
98
      ..y = _axis1.normalize()
3✔
99
      ..z = _axis2.normalize();
3✔
100
  }
101

102
  /// Transform this by the transform [t].
103
  void transform(Matrix4 t) {
1✔
104
    t
105
      ..transform3(_center)
2✔
106
      ..rotate3(_axis0..scale(_halfExtents.x))
5✔
107
      ..rotate3(_axis1..scale(_halfExtents.y))
5✔
108
      ..rotate3(_axis2..scale(_halfExtents.z));
5✔
109

110
    _halfExtents
1✔
111
      ..x = _axis0.normalize()
3✔
112
      ..y = _axis1.normalize()
3✔
113
      ..z = _axis2.normalize();
3✔
114
  }
115

116
  /// Store the corner with [cornerIndex] in [corner].
117
  void copyCorner(int cornerIndex, Vector3 corner) {
1✔
118
    assert(cornerIndex >= 0 || cornerIndex < 8);
2✔
119

120
    corner.setFrom(_center);
2✔
121

122
    switch (cornerIndex) {
123
      case 0:
1✔
124
        corner
125
          ..addScaled(_axis0, -_halfExtents.x)
5✔
126
          ..addScaled(_axis1, -_halfExtents.y)
5✔
127
          ..addScaled(_axis2, -_halfExtents.z);
5✔
128
        break;
129
      case 1:
1✔
130
        corner
131
          ..addScaled(_axis0, -_halfExtents.x)
5✔
132
          ..addScaled(_axis1, -_halfExtents.y)
5✔
133
          ..addScaled(_axis2, _halfExtents.z);
4✔
134
        break;
135
      case 2:
1✔
136
        corner
137
          ..addScaled(_axis0, -_halfExtents.x)
5✔
138
          ..addScaled(_axis1, _halfExtents.y)
4✔
139
          ..addScaled(_axis2, -_halfExtents.z);
5✔
140
        break;
141
      case 3:
1✔
142
        corner
143
          ..addScaled(_axis0, -_halfExtents.x)
5✔
144
          ..addScaled(_axis1, _halfExtents.y)
4✔
145
          ..addScaled(_axis2, _halfExtents.z);
4✔
146
        break;
147
      case 4:
1✔
148
        corner
149
          ..addScaled(_axis0, _halfExtents.x)
4✔
150
          ..addScaled(_axis1, -_halfExtents.y)
5✔
151
          ..addScaled(_axis2, -_halfExtents.z);
5✔
152
        break;
153
      case 5:
1✔
154
        corner
155
          ..addScaled(_axis0, _halfExtents.x)
4✔
156
          ..addScaled(_axis1, -_halfExtents.y)
5✔
157
          ..addScaled(_axis2, _halfExtents.z);
4✔
158
        break;
159
      case 6:
1✔
160
        corner
161
          ..addScaled(_axis0, _halfExtents.x)
4✔
162
          ..addScaled(_axis1, _halfExtents.y)
4✔
163
          ..addScaled(_axis2, -_halfExtents.z);
5✔
164
        break;
165
      case 7:
1✔
166
        corner
167
          ..addScaled(_axis0, _halfExtents.x)
4✔
168
          ..addScaled(_axis1, _halfExtents.y)
4✔
169
          ..addScaled(_axis2, _halfExtents.z);
4✔
170
        break;
171
    }
172
  }
173

174
  /// Find the closest point [q] on the OBB to the point [p] and store it in
175
  /// [q].
176
  void closestPointTo(Vector3 p, Vector3 q) {
1✔
177
    final d = p - _center;
2✔
178

179
    q.setFrom(_center);
2✔
180

181
    var dist = d.dot(_axis0);
2✔
182
    dist = dist.clamp(-_halfExtents.x, _halfExtents.x).toDouble();
7✔
183
    q.addScaled(_axis0, dist);
2✔
184

185
    dist = d.dot(_axis1);
2✔
186
    dist = dist.clamp(-_halfExtents.y, _halfExtents.y).toDouble();
7✔
187
    q.addScaled(_axis1, dist);
2✔
188

189
    dist = d.dot(_axis2);
2✔
190
    dist = dist.clamp(-_halfExtents.z, _halfExtents.z).toDouble();
7✔
191
    q.addScaled(_axis2, dist);
2✔
192
  }
193

194
  // Avoid allocating these instance on every call to intersectsWithObb3
195
  static final _r = Matrix3.zero();
3✔
196
  static final _absR = Matrix3.zero();
3✔
197
  static final _t = Vector3.zero();
3✔
198

199
  /// Check for intersection between this and [other].
200
  bool intersectsWithObb3(Obb3 other, [double epsilon = 1e-3]) {
1✔
201
    // Compute rotation matrix expressing other in this's coordinate frame
202
    _r
1✔
203
      ..setEntry(0, 0, _axis0.dot(other._axis0))
4✔
204
      ..setEntry(1, 0, _axis1.dot(other._axis0))
4✔
205
      ..setEntry(2, 0, _axis2.dot(other._axis0))
4✔
206
      ..setEntry(0, 1, _axis0.dot(other._axis1))
4✔
207
      ..setEntry(1, 1, _axis1.dot(other._axis1))
4✔
208
      ..setEntry(2, 1, _axis2.dot(other._axis1))
4✔
209
      ..setEntry(0, 2, _axis0.dot(other._axis2))
4✔
210
      ..setEntry(1, 2, _axis1.dot(other._axis2))
4✔
211
      ..setEntry(2, 2, _axis2.dot(other._axis2));
4✔
212

213
    // Compute translation vector t
214
    _t
1✔
215
      ..setFrom(other._center)
2✔
216
      ..sub(_center);
2✔
217

218
    // Bring translation into this's coordinate frame
219
    _t.setValues(_t.dot(_axis0), _t.dot(_axis1), _t.dot(_axis2));
11✔
220

221
    // Compute common subexpressions. Add in an epsilon term to
222
    // counteract arithmetic errors when two edges are parallel and
223
    // their cross product is (near) null.
224
    for (var i = 0; i < 3; i++) {
2✔
225
      for (var j = 0; j < 3; j++) {
2✔
226
        _absR.setEntry(i, j, _r.entry(i, j).abs() + epsilon);
6✔
227
      }
228
    }
229

230
    double ra;
231
    double rb;
232

233
    // Test axes L = A0, L = A1, L = A2
234
    for (var i = 0; i < 3; i++) {
2✔
235
      ra = _halfExtents[i];
2✔
236
      rb =
237
          other._halfExtents[0] * _absR.entry(i, 0) +
6✔
238
          other._halfExtents[1] * _absR.entry(i, 1) +
6✔
239
          other._halfExtents[2] * _absR.entry(i, 2);
5✔
240

241
      if (_t[i].abs() > ra + rb) {
5✔
242
        return false;
243
      }
244
    }
245

246
    // Test axes L = B0, L = B1, L = B2
247
    for (var i = 0; i < 3; i++) {
2✔
248
      ra =
249
          _halfExtents[0] * _absR.entry(0, i) +
6✔
250
          _halfExtents[1] * _absR.entry(1, i) +
6✔
251
          _halfExtents[2] * _absR.entry(2, i);
5✔
252
      rb = other._halfExtents[i];
2✔
253

254
      if ((_t[0] * _r.entry(0, i) +
6✔
255
                  _t[1] * _r.entry(1, i) +
6✔
256
                  _t[2] * _r.entry(2, i))
5✔
257
              .abs() >
2✔
258
          ra + rb) {
1✔
259
        return false;
260
      }
261
    }
262

263
    // Test axis L = A0 x B0
264
    ra =
265
        _halfExtents[1] * _absR.entry(2, 0) +
6✔
266
        _halfExtents[2] * _absR.entry(1, 0);
5✔
267
    rb =
268
        other._halfExtents[1] * _absR.entry(0, 2) +
6✔
269
        other._halfExtents[2] * _absR.entry(0, 1);
5✔
270
    if ((_t[2] * _r.entry(1, 0) - _t[1] * _r.entry(2, 0)).abs() > ra + rb) {
14✔
271
      return false;
272
    }
273

274
    // Test axis L = A0 x B1
275
    ra =
276
        _halfExtents[1] * _absR.entry(2, 1) +
6✔
277
        _halfExtents[2] * _absR.entry(1, 1);
5✔
278
    rb =
279
        other._halfExtents[0] * _absR.entry(0, 2) +
6✔
280
        other._halfExtents[2] * _absR.entry(0, 0);
5✔
281
    if ((_t[2] * _r.entry(1, 1) - _t[1] * _r.entry(2, 1)).abs() > ra + rb) {
14✔
282
      return false;
283
    }
284

285
    // Test axis L = A0 x B2
286
    ra =
287
        _halfExtents[1] * _absR.entry(2, 2) +
6✔
288
        _halfExtents[2] * _absR.entry(1, 2);
5✔
289
    rb =
290
        other._halfExtents[0] * _absR.entry(0, 1) +
6✔
291
        other._halfExtents[1] * _absR.entry(0, 0);
5✔
292
    if ((_t[2] * _r.entry(1, 2) - _t[1] * _r.entry(2, 2)).abs() > ra + rb) {
14✔
293
      return false;
294
    }
295

296
    // Test axis L = A1 x B0
297
    ra =
298
        _halfExtents[0] * _absR.entry(2, 0) +
6✔
299
        _halfExtents[2] * _absR.entry(0, 0);
5✔
300
    rb =
301
        other._halfExtents[1] * _absR.entry(1, 2) +
6✔
302
        other._halfExtents[2] * _absR.entry(1, 1);
5✔
303
    if ((_t[0] * _r.entry(2, 0) - _t[2] * _r.entry(0, 0)).abs() > ra + rb) {
14✔
304
      return false;
305
    }
306

307
    // Test axis L = A1 x B1
308
    ra =
309
        _halfExtents[0] * _absR.entry(2, 1) +
6✔
310
        _halfExtents[2] * _absR.entry(0, 1);
5✔
311
    rb =
312
        other._halfExtents[0] * _absR.entry(1, 2) +
6✔
313
        other._halfExtents[2] * _absR.entry(1, 0);
5✔
314
    if ((_t[0] * _r.entry(2, 1) - _t[2] * _r.entry(0, 1)).abs() > ra + rb) {
14✔
315
      return false;
316
    }
317

318
    // Test axis L = A1 x B2
319
    ra =
320
        _halfExtents[0] * _absR.entry(2, 2) +
6✔
321
        _halfExtents[2] * _absR.entry(0, 2);
5✔
322
    rb =
323
        other._halfExtents[0] * _absR.entry(1, 1) +
6✔
324
        other._halfExtents[1] * _absR.entry(1, 0);
5✔
325
    if ((_t[0] * _r.entry(2, 2) - _t[2] * _r.entry(0, 2)).abs() > ra + rb) {
14✔
326
      return false;
327
    }
328

329
    // Test axis L = A2 x B0
330
    ra =
331
        _halfExtents[0] * _absR.entry(1, 0) +
6✔
332
        _halfExtents[1] * _absR.entry(0, 0);
5✔
333
    rb =
334
        other._halfExtents[1] * _absR.entry(2, 2) +
6✔
335
        other._halfExtents[2] * _absR.entry(2, 1);
5✔
336
    if ((_t[1] * _r.entry(0, 0) - _t[0] * _r.entry(1, 0)).abs() > ra + rb) {
14✔
337
      return false;
338
    }
339

340
    // Test axis L = A2 x B1
341
    ra =
342
        _halfExtents[0] * _absR.entry(1, 1) +
6✔
343
        _halfExtents[1] * _absR.entry(0, 1);
5✔
344
    rb =
345
        other._halfExtents[0] * _absR.entry(2, 2) +
6✔
346
        other._halfExtents[2] * _absR.entry(2, 0);
5✔
347
    if ((_t[1] * _r.entry(0, 1) - _t[0] * _r.entry(1, 1)).abs() > ra + rb) {
14✔
348
      return false;
349
    }
350

351
    // Test axis L = A2 x B2
352
    ra =
353
        _halfExtents[0] * _absR.entry(1, 2) +
6✔
354
        _halfExtents[1] * _absR.entry(0, 2);
5✔
355
    rb =
356
        other._halfExtents[0] * _absR.entry(2, 1) +
6✔
357
        other._halfExtents[1] * _absR.entry(2, 0);
5✔
358
    if ((_t[1] * _r.entry(0, 2) - _t[0] * _r.entry(1, 2)).abs() > ra + rb) {
14✔
359
      return false;
360
    }
361

362
    // Since no separating axis is found, the OBBs must be intersecting
363
    return true;
364
  }
365

366
  // Avoid allocating these instance on every call to intersectsWithTriangle
367
  static final _triangle = Triangle();
3✔
368
  static final _aabb3 = Aabb3();
3✔
369
  static final _zeroVector = Vector3.zero();
3✔
370

371
  /// Return if this intersects with [other]
372
  bool intersectsWithTriangle(Triangle other, {IntersectionResult? result}) {
1✔
373
    _triangle.copyFrom(other);
2✔
374

375
    _triangle.point0
2✔
376
      ..sub(_center)
2✔
377
      ..setValues(
1✔
378
        _triangle.point0.dot(axis0),
4✔
379
        _triangle.point0.dot(axis1),
4✔
380
        _triangle.point0.dot(axis2),
4✔
381
      );
382
    _triangle.point1
2✔
383
      ..sub(_center)
2✔
384
      ..setValues(
1✔
385
        _triangle.point1.dot(axis0),
4✔
386
        _triangle.point1.dot(axis1),
4✔
387
        _triangle.point1.dot(axis2),
4✔
388
      );
389
    _triangle.point2
2✔
390
      ..sub(_center)
2✔
391
      ..setValues(
1✔
392
        _triangle.point2.dot(axis0),
4✔
393
        _triangle.point2.dot(axis1),
4✔
394
        _triangle.point2.dot(axis2),
4✔
395
      );
396

397
    _aabb3.setCenterAndHalfExtents(_zeroVector, _halfExtents);
4✔
398

399
    return _aabb3.intersectsWithTriangle(_triangle, result: result);
3✔
400
  }
401

402
  // Avoid allocating these instance on every call to intersectsWithVector3
403
  static final _vector = Vector3.zero();
3✔
404

405
  /// Return if this intersects with [other]
406
  bool intersectsWithVector3(Vector3 other) {
1✔
407
    _vector
1✔
408
      ..setFrom(other)
1✔
409
      ..sub(_center)
2✔
410
      ..setValues(_vector.dot(axis0), _vector.dot(axis1), _vector.dot(axis2));
10✔
411

412
    _aabb3.setCenterAndHalfExtents(_zeroVector, _halfExtents);
4✔
413

414
    return _aabb3.intersectsWithVector3(_vector);
3✔
415
  }
416

417
  // Avoid allocating these instance on every call to intersectsWithTriangle
418
  static final _quadTriangle0 = Triangle();
×
419
  static final _quadTriangle1 = Triangle();
×
420

421
  /// Return if this intersects with [other]
422
  bool intersectsWithQuad(Quad other, {IntersectionResult? result}) {
×
423
    other.copyTriangles(_quadTriangle0, _quadTriangle1);
×
424

425
    return intersectsWithTriangle(_quadTriangle0, result: result) ||
×
426
        intersectsWithTriangle(_quadTriangle1, result: result);
×
427
  }
428
}
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