def test_bbox_is_frozen() -> None: """Test modification is not possible.""" bb = BBox(40, 60, 80, 120, 450, 730, contents=CollideType.PHYSICS) with pytest.raises(AttributeError): bb.min_x = 100 with pytest.raises(AttributeError): bb.min_y = 100 with pytest.raises(AttributeError): bb.min_z = 100 with pytest.raises(AttributeError): bb.max_x = 100 with pytest.raises(AttributeError): bb.max_y = 100 with pytest.raises(AttributeError): bb.max_z = 100 with pytest.raises(AttributeError): bb.contents = CollideType.GRATE with pytest.raises(AttributeError): bb.tags = frozenset({'tag1', 'tag2', 'tag3'}) with pytest.raises(AttributeError): bb.tags.add('extra') # Check all these assignments didn't actually do anything. assert_bbox(bb, (40, 60, 80), (120, 450, 730), CollideType.PHYSICS, set())
def test_illegal_bbox() -> None: """A line or point segement is not allowed.""" with pytest.raises(ValueError): BBox(Vec(1, 2, 3), Vec(1, 2, 3)) with pytest.raises(ValueError): BBox(Vec(1, 2, 3), Vec(10, 2, 3)) with pytest.raises(ValueError): BBox(Vec(1, 2, 3), Vec(1, 20, 3)) with pytest.raises(ValueError): BBox(Vec(1, 2, 3), Vec(1, 2, 30))
def test_bbox_construction() -> None: bb = BBox(Vec(1, 2, 3), Vec(4, 5, 6)) # Check assert_bbox() is correct. assert bb.min_x == 1 assert bb.min_y == 2 assert bb.min_z == 3 assert bb.max_x == 4 assert bb.max_y == 5 assert bb.max_z == 6 assert bb.contents is CollideType.SOLID assert_bbox(bb, (1, 2, 3), (4, 5, 6), CollideType.SOLID, set()) assert_bbox( BBox(Vec(4.1, 1.9, 6), Vec(1, 5.1, 2.85), contents=CollideType.FIZZLER | CollideType.ANTLINES), (1, 2, 3), (4, 5, 6), CollideType.FIZZLER | CollideType.ANTLINES, set(), ) assert_bbox( BBox(-50, 80, -60, 30, -40, 95, contents=CollideType.GLASS, tags='tag1'), (-50, -40, -60), (30, 80, 95), CollideType.GLASS, {'tag1', }, ) plane_x = BBox(80, 90.0, 10, 80.0, 250, 40.0, contents=CollideType.GRATE) assert plane_x.is_plane assert plane_x.plane_normal == Vec(1, 0, 0) assert_bbox(plane_x, (80, 90, 10), (80, 250, 40), CollideType.GRATE, set()) plane_x = BBox(80.0, 90.0, 10, 80, 250, 40.0, contents=CollideType.GRATE) assert plane_x.is_plane assert plane_x.plane_normal == Vec(1, 0, 0) assert_bbox(plane_x, (80, 90, 10), (80, 250, 40), CollideType.GRATE, set()) plane_y = BBox(80, 250, 10.0, 110.0, 250, 40, contents=CollideType.GRATE) assert plane_y.is_plane assert plane_y.plane_normal == Vec(0, 1, 0) assert_bbox(plane_y, (80, 250, 10), (110, 250, 40), CollideType.GRATE, set()) plane_z = BBox(80, 250, 40, 110, 90, 40, contents=CollideType.GRATE) assert plane_z.is_plane assert plane_z.plane_normal == Vec(0, 0, 1) assert_bbox(plane_z, (80, 90, 40), (110, 250, 40), CollideType.GRATE, set()) assert BBox(-10, -10, -10, +10, +10, +10).tags == frozenset({}) assert BBox( -10, -10, -10, +10, +10, +10, contents=CollideType.BRIDGE, tags='tags', # Special case, not iterated. ).tags == frozenset({'tags'}) assert BBox( -10, -10, -10, +10, +10, +10, tags=['a', 'b', 'embed'], ).tags == frozenset({'a', 'b', 'embed'})
def test_bbox_parse_block() -> None: """Test parsing of a block-shaped bbox from a VMF.""" vmf = VMF() ent = vmf.create_ent( 'bee2_collision_bbox', coll_deco=1, coll_physics=1, coll_grating=0, tags='standard excellent', ) ent.solids.append(vmf.make_prism(Vec(80, 10, 40), Vec(150, 220, 70)).solid) ent.solids.append(vmf.make_prism(Vec(-30, 45, 80), Vec(-20, 60, 120)).solid) bb2, bb1 = BBox.from_ent(ent) # Allow it to produce in either order. if bb1.min_x == -30: bb1, bb2 = bb2, bb1 assert_bbox( bb1, (80, 10, 40), (150, 220, 70), CollideType.DECORATION | CollideType.PHYSICS, {'standard', 'excellent'}, ) assert_bbox( bb2, (-30, 45, 80), (-20, 60, 120), CollideType.DECORATION | CollideType.PHYSICS, {'standard', 'excellent'}, )
def test_bbox_hash() -> None: """Test hashability of bboxes.""" bb = BBox(40, 60, 80, 120, 450, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'}) hash(bb) # Check it can be hashed. # Check each value changes the hash. assert hash(bb) != hash(BBox(45, 40, 80, 120, 450, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 59, 80, 120, 450, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 81, 120, 450, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 80, 121, 450, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 80, 120, 455, 730, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 80, 120, 450, 732, contents=CollideType.PHYSICS, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 80, 120, 450, 730, contents=CollideType.ANTLINES, tags={'tag1', 'tag2'})) assert hash(bb) != hash(BBox(40, 60, 80, 120, 450, 732, contents=CollideType.PHYSICS, tags={'tag1', 'tag3'}))
def test_bbox_intersection( mins: tuple3, maxs: tuple3, x: int, y: int, z: int, success: tuple[tuple3, tuple3] | None, axes: str, ) -> None: """Test intersection founction for bounding boxes. We parameterise by swapping all the axes, and offsetting so it's in all the quadrants. """ bbox1 = BBox(x-64, y-64, z-64, x+64, y+64, z+64, contents=CollideType.EVERYTHING) bbox2 = BBox(reorder(mins, axes, x, y, z), reorder(maxs, axes, x, y, z), contents=CollideType.EVERYTHING) result = bbox1.intersect(bbox2) # assert result == bbox2.intersect(bbox1) # Check order is irrelevant. if success is None: assert result is None else: exp_a, exp_b = success expected = BBox(reorder(exp_a, axes, x, y, z), reorder(exp_b, axes, x, y, z), contents=CollideType.EVERYTHING) assert result == expected
def test_bbox_parse_plane(axis: str, mins: tuple3, maxes: tuple3) -> None: """Test parsing planar bboxes from a VMF. With 5 skip sides, the brush is flattened into the remaining plane. """ vmf = VMF() ent = vmf.create_ent('bee2_collision_bbox', coll_solid=1) prism = vmf.make_prism(Vec(80, 10, 40), Vec(150, 220, 70), mat='tools/toolsskip') getattr(prism, axis).mat = 'tools/toolsclip' ent.solids.append(prism.solid) [bbox] = BBox.from_ent(ent) assert_bbox(bbox, mins, maxes, CollideType.SOLID, set())
def test_bbox_vecs() -> None: """Test that the vector properties don't return the same object.""" bb = BBox(30, 60, 80, 120, 451, 730) assert bb.mins == Vec(30.0, 60.0, 80.0) assert bb.mins is not bb.mins assert bb.maxes == Vec(120.0, 451.0, 730.0) assert bb.maxes is not bb.maxes assert bb.size == Vec(90.0, 391.0, 650.0) assert bb.size is not bb.size assert bb.center == Vec(75.0, 255.5, 405.0) assert bb.center is not bb.center
def test_bbox_rotation( pitch: float, yaw: float, roll: float, ) -> None: """Test the rotation logic against the slow direct approach.""" ang = Angle(pitch, yaw, roll) bb_start = BBox(100, 200, 300, 300, 450, 600, contents=CollideType.ANTLINES, tags='blah') # Directly compute, by rotating all the angles, points = [ Vec(x, y, z) for x in [100, 300] for y in [200, 450] for z in [300, 600] ] result_ang = bb_start @ ang result_mat = bb_start @ Matrix.from_angle(ang) assert result_ang == result_mat bb_min, bb_max = Vec.bbox( point @ ang for point in points ) assert_bbox(result_mat, round(bb_min, 0), round(bb_max, 0), CollideType.ANTLINES, {'blah'})
def test_bbox_addition() -> None: """Test adding to bbox to shift them around.""" bb = BBox(40, 60, 80, 120, 450, 730, contents=CollideType.ANTLINES, tags={'a', 'b'}) assert_bbox( bb + Vec(10, -30, 45), (50, 30, 125), (130, 420, 775), CollideType.ANTLINES, {'a', 'b'} ) assert_bbox( bb + (10, -30, 45), (50, 30, 125), (130, 420, 775), CollideType.ANTLINES, {'a', 'b'} ) assert_bbox( bb - Vec(10, 20, -15), (30, 40, 95), (110, 430, 745), CollideType.ANTLINES, {'a', 'b'} ) assert_bbox( bb - (10, 20, -15), (30, 40, 95), (110, 430, 745), CollideType.ANTLINES, {'a', 'b'} )
def load_collision_bbox(item: Item, ent: Entity) -> None: """Load precise BEE collisions.""" item.collisions.extend(BBox.from_ent(ent))