feat: add cesium traffic signal countdowns
This commit is contained in:
124
blender/tests/test_traffic_signals.py
Normal file
124
blender/tests/test_traffic_signals.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""Static traffic-signal geometry can be exercised without Blender itself."""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
|
||||
|
||||
class FakeBatch:
|
||||
created = []
|
||||
|
||||
def __init__(self, name, collection, material):
|
||||
self.name = name
|
||||
self.vertices = []
|
||||
self.faces = []
|
||||
FakeBatch.created.append(self)
|
||||
|
||||
def add_prism(self, ring, base, height):
|
||||
if len(ring) < 3:
|
||||
return
|
||||
start = len(self.vertices)
|
||||
self.vertices.extend((x, y, base) for x, y in ring)
|
||||
self.vertices.extend((x, y, base + height) for x, y in ring)
|
||||
size = len(ring)
|
||||
self.faces.extend((tuple(range(start, start + size)),
|
||||
tuple(range(start + size, start + size * 2))))
|
||||
|
||||
def finish(self):
|
||||
return self.vertices or None
|
||||
|
||||
|
||||
class Projector:
|
||||
def xy(self, point):
|
||||
return point
|
||||
|
||||
|
||||
class TrafficSignalGeometryTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
mesh = types.ModuleType("osmassets.mesh")
|
||||
mesh.MeshBatch = FakeBatch
|
||||
cls.previous_mesh = sys.modules.get("osmassets.mesh")
|
||||
sys.modules["osmassets.mesh"] = mesh
|
||||
sys.modules.pop("osmassets.traffic_signals", None)
|
||||
cls.signals = importlib.import_module("osmassets.traffic_signals")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
sys.modules.pop("osmassets.traffic_signals", None)
|
||||
if cls.previous_mesh is None:
|
||||
sys.modules.pop("osmassets.mesh", None)
|
||||
else:
|
||||
sys.modules["osmassets.mesh"] = cls.previous_mesh
|
||||
|
||||
def test_valid_anchor_builds_static_geometry_on_the_driver_right(self):
|
||||
FakeBatch.created = []
|
||||
count = self.signals.assemble({
|
||||
"layout": {"countdownLateralMeters": 1.15},
|
||||
"signals": [{
|
||||
"longitude": 10.0,
|
||||
"latitude": 20.0,
|
||||
"headingDegrees": 0.0,
|
||||
"mastReachMeters": 4.5,
|
||||
}],
|
||||
}, Projector(), object(), {
|
||||
"metal": object(),
|
||||
"housing": object(),
|
||||
"lenses": {"red": object(), "yellow": object(), "green": object()},
|
||||
})
|
||||
self.assertEqual(count, 1)
|
||||
housing = next(batch for batch in FakeBatch.created
|
||||
if batch.name == "Traffic Signal Housing")
|
||||
# A northbound driver's right is east, so the board's vertices must
|
||||
# extend east of the mast-reached head at longitude 5.5.
|
||||
self.assertGreater(max(vertex[0] for vertex in housing.vertices), 6.5)
|
||||
red_lens = next(batch for batch in FakeBatch.created
|
||||
if batch.name == "Traffic Signal Red Lens")
|
||||
# The mast arm and the head share z=6.25. The red lens sits inside
|
||||
# the top half of the 1.62m head rather than above its centre line.
|
||||
self.assertLessEqual(max(vertex[2] for vertex in red_lens.vertices), 6.98)
|
||||
|
||||
def test_missing_anchor_coordinate_is_skipped(self):
|
||||
FakeBatch.created = []
|
||||
count = self.signals.assemble({"signals": [{"longitude": 10.0}]}, Projector(),
|
||||
object(), {"metal": object(), "housing": object(),
|
||||
"lenses": {"red": object(), "yellow": object(), "green": object()}})
|
||||
self.assertEqual(count, 0)
|
||||
|
||||
def test_dynamic_lens_geometry_is_in_front_of_static_lens_face(self):
|
||||
FakeBatch.created = []
|
||||
signal = {
|
||||
"id": "signal-1", "longitude": 10.0, "latitude": 20.0,
|
||||
"headingDegrees": 0.0,
|
||||
"pose": {
|
||||
"pole": {"longitude": 10.0, "latitude": 20.0},
|
||||
"head": {"longitude": 10.0, "latitude": 20.0,
|
||||
"faceHeadingDegrees": 0.0},
|
||||
"lenses": [{"state": state, "longitude": 10.0,
|
||||
"latitude": 20.0, "height": 6.25}
|
||||
for state in ("red", "yellow", "green")],
|
||||
"countdown": {"longitude": 10.0, "latitude": 20.0,
|
||||
"height": 6.25},
|
||||
},
|
||||
}
|
||||
self.signals.assemble_dynamic({"signals": [signal]}, Projector(), object(), {
|
||||
"red": object(), "yellow": object(), "green": object(), "active": object(),
|
||||
"countdown": {0: object(), 1: object()},
|
||||
})
|
||||
red = next(batch for batch in FakeBatch.created
|
||||
if batch.name == "TrafficSignalDynamic_signal-1_red")
|
||||
# Facing north, every active overlay vertex must sit north of the
|
||||
# static lens centre rather than intersecting its body.
|
||||
self.assertGreater(min(vertex[1] for vertex in red.vertices), 20.035)
|
||||
|
||||
def test_countdown_uses_the_versioned_font_and_twenty_shared_values(self):
|
||||
self.assertTrue(os.path.exists(self.signals.COUNTDOWN_FONT_PATH))
|
||||
self.assertEqual(self.signals.COUNTDOWN_VALUES, tuple("%02d" % value for value in range(20)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user