def surface(self, param):
        """Return the detector surface point corresponding to ``param``.

        For a parameter ``phi``, the returned point is given by ::

            surf = R * radius * (cos(phi), -sin(phi)) + t

        where ``R`` is a rotation matrix and ``t`` is a translation vector.
        Note that increase of ``phi`` corresponds to rotation
        in the clockwise direction, by analogy to flat detectors.

        Parameters
        ----------
        param : float or `array-like`
            Parameter value(s) at which to evaluate.

        Returns
        -------
        point : `numpy.ndarray`
            Vector(s) pointing from the origin to the detector surface
            point at ``param``.
            If ``param`` is a single parameter, the returned array has
            shape ``(2,)``, otherwise ``param.shape + (2,)``.

        Examples
        --------
        The method works with a single parameter, resulting in a single
        vector:

        >>> part = odl.uniform_partition(-np.pi / 2, np.pi / 2, 10)
        >>> det = CircularDetector(part, axis=[1, 0], radius=2)
        >>> np.allclose(det.surface(0), [0, 0])
        True

        It is also vectorized, i.e., it can be called with multiple
        parameters at once (or an n-dimensional array of parameters):

        >>> np.round(det.surface([-np.pi / 2, 0, np.pi / 2]), 10)
        array([[-2., -2.],
               [ 0.,  0.],
               [ 2., -2.]])

        >>> det.surface(np.zeros((4, 5))).shape
        (4, 5, 2)
        """
        squeeze_out = (np.shape(param) == ())
        param = np.array(param, dtype=float, copy=False, ndmin=1)
        if self.check_bounds and not is_inside_bounds(param, self.params):
            raise ValueError('`param` {} not in the valid range '
                             '{}'.format(param, self.params))

        surf = np.empty(param.shape + (2,))
        surf[..., 0] = np.cos(param)
        surf[..., 1] = -np.sin(param)
        surf *= self.radius
        surf = np.matmul(surf, np.transpose(self.rotation_matrix))
        surf += self.translation
        if squeeze_out:
            surf = surf.squeeze()

        return surf
Return the detector surface point corresponding to ``param``.

        For a parameter ``phi``, the returned point is given by ::

            surf = R * radius * (cos(phi), -sin(phi)) + t

        where ``R`` is a rotation matrix and ``t`` is a translation vector.
        Note that increase of ``phi`` corresponds to rotation
        in the clockwise direction, by analogy to flat detectors.

        Parameters
        ----------
        param : float or `array-like`
            Parameter value(s) at which to evaluate.

        Returns
        -------
        point : `numpy.ndarray`
            Vector(s) pointing from the origin to the detector surface
            point at ``param``.
            If ``param`` is a single parameter, the returned array has
            shape ``(2,)``, otherwise ``param.shape + (2,)``.

        Examples
        --------
        The method works with a single parameter, resulting in a single
        vector:

        >>> part = odl.uniform_partition(-np.pi / 2, np.pi / 2, 10)
        >>> det = CircularDetector(part, axis=[1, 0], radius=2)
        >>> np.allclose(det.surface(0), [0, 0])
        True

        It is also vectorized, i.e., it can be called with multiple
        parameters at once (or an n-dimensional array of parameters):

        >>> np.round(det.surface([-np.pi / 2, 0, np.pi / 2]), 10)
        array([[-2., -2.],
               [ 0.,  0.],
               [ 2., -2.]])

        >>> det.surface(np.zeros((4, 5))).shape
        (4, 5, 2)