diff --git a/src/scenic/core/object_types.py b/src/scenic/core/object_types.py index a630a2c41..f6bdaddef 100644 --- a/src/scenic/core/object_types.py +++ b/src/scenic/core/object_types.py @@ -1081,7 +1081,11 @@ class Object(OrientedPoint): "showVisibleRegion": False, "color": None, "render": True, - "velocity": PropertyDefault((), {"dynamic"}, lambda self: Vector(0, 0, 0)), + "velocity": PropertyDefault( + ("speed", "orientation"), + {"dynamic"}, + lambda self: Vector(0, self.speed, 0).rotatedBy(self.orientation), + ), "speed": PropertyDefault((), {"dynamic"}, lambda self: 0), "angularVelocity": PropertyDefault((), {"dynamic"}, lambda self: Vector(0, 0, 0)), "angularSpeed": PropertyDefault((), {"dynamic"}, lambda self: 0), diff --git a/tests/syntax/test_specifiers.py b/tests/syntax/test_specifiers.py index 274e875e1..2fd7c127c 100644 --- a/tests/syntax/test_specifiers.py +++ b/tests/syntax/test_specifiers.py @@ -87,6 +87,21 @@ def test_lazy_value_in_requirement_2(): sampleScene(scenario, maxIterations=1) +def test_default_velocity_depends_on_speed_and_orientation(): + # Heading convention in Scenic: 0 deg = +Y, 90 deg = -X + ego = sampleEgoFrom("ego = new Object with speed 10, facing 90 deg") + assert tuple(ego.velocity) == pytest.approx((-10, 0, 0)) + + # Order independence (dependency graph should resolve the same way) + ego = sampleEgoFrom("ego = new Object facing 90 deg, with speed 10") + assert tuple(ego.velocity) == pytest.approx((-10, 0, 0)) + + # Orientation derived from another specifier (not a literal heading) + ego = sampleEgoFrom("ego = new Object with speed 10, facing toward -1 @ 1") + s = 10 * math.sqrt(0.5) + assert tuple(ego.velocity) == pytest.approx((-s, s, 0)) + + ## Value normalization