Skip to content

Conversation

@mberz
Copy link
Member

@mberz mberz commented Nov 19, 2025

Changes proposed in this pull request:

  • Rename RotationSH to SphericalHarmonicDefintion
  • Use SphericalHarmonicDefintion instead of n_max
  • allow applying to SphericalHarmonic* data objects
  • overload multiplication operator
  • update and extend documentation

Requires

@mberz mberz added this to the v1.0.0 milestone Nov 19, 2025
@mberz mberz self-assigned this Nov 19, 2025
@mberz mberz moved this from Backlog to Implementation in progress in Weekly Planning Nov 19, 2025
@mberz mberz requested a review from Copilot November 28, 2025 16:06
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors the rotation functionality to use the SphericalHarmonicDefinition class for defining spherical harmonic properties instead of passing n_max as a parameter.

  • Renamed RotationSH class to SphericalHarmonicRotation for better naming consistency
  • Refactored rotation matrix generation to use SphericalHarmonicDefinition objects instead of separate n_max and basis_type parameters
  • Updated rotation methods to work with SphericalHarmonicSignal objects via duck typing

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
spharpy/transforms/rotations.py Renamed RotationSH to SphericalHarmonicRotation, refactored to use SphericalHarmonicDefinition for rotation matrix creation, simplified class by removing explicit classmethod overrides, added __mul__ operator for rotation composition and signal rotation
spharpy/transforms/init.py Updated exports to use new SphericalHarmonicRotation class name
tests/test_rotations.py Updated tests to use new class name and API, added new tests for apply method and rotation multiplication operator

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Copy link
Member

@f-brinkmann f-brinkmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Conceptually everything is fine to me. I left a few comments on the docs already but did not review the tests, yet.

@mberz mberz force-pushed the refactor/rotation_adapt_sh_definition branch 2 times, most recently from ad94866 to 345c174 Compare December 17, 2025 07:35
@mberz mberz force-pushed the refactor/rotation_adapt_sh_definition branch from 345c174 to 522bafb Compare December 17, 2025 07:37
@mberz mberz marked this pull request as ready for review December 17, 2025 16:41
Copy link
Member

@f-brinkmann f-brinkmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is great to have! Code looks good to me. I left some comments on how the docs might be improved and have thought for discussion:

We have to ways to apply a rotation to a SHAudio object and it might be a little confusing that * is used instead of @. Do we want to keep both ways? Do we want to use * or doe we want / could we use @?

I would suggest to open an issue to move this to the classes module. In this case

  • the example in the docstring of the class could be moved above the class
  • In the module docstring of the transforms module, I would suggest to mention, that these are helper functions and that we recomment to use the SHRotation class

class SphericalHarmonicRotation(ScipyRotation):
"""Class for rotations of coordinates and spherical harmonic expansions.
The class extends the :py:class:`~scipy.spatial.transform.Rotation` class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The class extends the :py:class:`~scipy.spatial.transform.Rotation` class
The class extends the SciPy :py:class:`~scipy.spatial.transform.Rotation` class

Comment on lines +137 to +138
.. [#] B. Rafaely, Fundamentals of Spherical Array Processing, 1st ed.,
vol. 8. Springer-Verlag GmbH Berlin Heidelberg, 2015.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should cite the second edition:

Suggested change
.. [#] B. Rafaely, Fundamentals of Spherical Array Processing, 1st ed.,
vol. 8. Springer-Verlag GmbH Berlin Heidelberg, 2015.
.. [#] B. Rafaely, Fundamentals of Spherical Array Processing, 2nd ed.,
vol. 8. Springer-Verlag GmbH Berlin Heidelberg, 2019.

Comment on lines +30 to +34
To create a rotation object, use one of the available
:py:meth:`~scipy.spatial.transform.Rotation.from_*` methods, e.g.,
:py:meth:`~scipy.spatial.transform.Rotation.from_euler` or
:py:meth:`~scipy.spatial.transform.Rotation.from_quat` for creation using
Euler angles or quaternions, respectively. Also see the examples below.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the examples follow directly below. I would suggest to delete this paragraph.

>>> 'z', angle)
The spherical harmonic rotation matrix can be obtained and applied
to the using the following:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to the using the following:
to the coefficients using the following:

>>> definition, coordinates=sampling)
...
>>> _, axs = plt.subplots(
>>> 1, 2, subplot_kw={'projection': '3d'}, figsize=(5, 2.5))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The axis labels are partly overlapping and clipped. Can you try to improve this, e.g., by adding space or increasing figure size?

The effect of the rotation can again be visualized by evaluating the
series expansion on the unit sphere:
>>> _, axs = plt.subplots(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above on overlapping/clipped labels.

Comment on lines +109 to +114
The rotation can now be applied using the `*` operator:
Note that due to the multiplication with itself, the rotation
is applied twice, which corresponds to a rotation of 90 degrees around
the z-axis.
>>> rotated_frequency_data = R * R * frequency_data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to show all ways in which a rotation can be applied in one spot. This would shorten the example and bundle information in one spot. For simplicity, I would only apply one rotation (and maybe make it 90 degrees, because the plot is more clear in this case)

Suggested change
The rotation can now be applied using the `*` operator:
Note that due to the multiplication with itself, the rotation
is applied twice, which corresponds to a rotation of 90 degrees around
the z-axis.
>>> rotated_frequency_data = R * R * frequency_data
The rotation can be applied in different ways. Mathematically it is applied
by a matrix multiplication of the roation matrix with the spherical harmonic
coefficients
>>> rotated_coefficients = R @ coefficients
This is the same as using the coefficients store in the spherical harmonic
FrequencyData obejct
>>> rotated_coefficients = R @ frequency_data.freq
However, it might be more convenient to keep the coefficients inside the
FrequencyData object and apply the roation using the `*` operator
operator
>>> rotated_frequency_data = R * frequency_data
which can also be done using the `apply` method
>>> rotated_frequency_data = R.apply(frequency_data)

"""Apply the rotation to spherical harmonic data object.
Note that the spherical harmonic definition of the target object is
used to generate a fitting spherical harmonic rotation matrix.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misses

Note
----
At the moment, only spherical harmonic rotations matrices following
the ``ACN`` indexing and ``N3D`` normalization are supported.

return M @ coefficients
raise ValueError(
"Multiplication is only supported for "
"SphericalHarmonicRotation and SphericalHarmonic* audio "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is clear withtou '*'

Suggested change
"SphericalHarmonicRotation and SphericalHarmonic* audio "
"SphericalHarmonicRotation and SphericalHarmonic audio "

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a brief dosctring to each test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Implementation in progress

Development

Successfully merging this pull request may close these issues.

3 participants