diff --git a/doc/changelog.rst b/doc/changelog.rst
index 6aa3b745..436ffce3 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -20,6 +20,7 @@ MJX
- Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
- Added ``TOUCH`` sensor.
- Added support for ``eq_active``. Fixes :github:issue:`2173`.
+- Added ray intersection with ellipsoid.
Bug fixes
^^^^^^^^^
diff --git a/mjx/mujoco/mjx/_src/ray.py b/mjx/mujoco/mjx/_src/ray.py
index 2591385a..9335f4e9 100644
--- a/mjx/mujoco/mjx/_src/ray.py
+++ b/mjx/mujoco/mjx/_src/ray.py
@@ -108,6 +108,29 @@ def _ray_capsule(
return x
+def _ray_ellipsoid(
+ size: jax.Array,
+ pnt: jax.Array,
+ vec: jax.Array,
+) -> jax.Array:
+ """Returns the distance at which a ray intersects with an ellipsoid."""
+
+ # invert size^2
+ s = 1 / jp.square(size)
+
+ # (x*lvec+lpnt)' * diag(1/size^2) * (x*lvec+lpnt) = 1
+ svec = s * vec
+ a = svec @ vec
+ b = svec @ pnt
+ c = (s * pnt) @ pnt - 1
+
+ # solve a*x^2 + 2*b*x + c = 0
+ x0, x1 = _ray_quad(a, b, c)
+ x = jp.where(jp.isinf(x0), x1, x0)
+
+ return x
+
+
def _ray_box(
size: jax.Array,
pnt: jax.Array,
@@ -201,6 +224,7 @@ _RAY_FUNC = {
GeomType.PLANE: _ray_plane,
GeomType.SPHERE: _ray_sphere,
GeomType.CAPSULE: _ray_capsule,
+ GeomType.ELLIPSOID: _ray_ellipsoid,
GeomType.BOX: _ray_box,
GeomType.MESH: _ray_mesh,
}
diff --git a/mjx/mujoco/mjx/test_data/sensor/sensor.xml b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
index 71532743..b6d3fa2a 100644
--- a/mjx/mujoco/mjx/test_data/sensor/sensor.xml
+++ b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
@@ -119,6 +119,11 @@