Skip to content

Circle.point_at_angle() misbehaves for circles with rotation / reflections #4236

Open
@melodysium

Description

@melodysium

Description of bug / unexpected behavior

After a rotation or reflection which moves a Circle's starting point away from the RIGHT axis, Circle.point_at_angle() returns incorrect results.

Expected behavior

  • When calling Circle.point_at_angle(), an angle of 0 should return the point at Circle.points[0], no matter how the Circle has been rotated or reflected.
  • When calling Circle.point_at_angle() with a nonzero angle, the sign of the angle value should correspond to the Circle's reflections (i.e. for an un-reflected Circle, a positive angle should proceed counter-clockwise; a positive angle on a reflected circle should proceed clockwise.

How to reproduce the issue

Code for reproducing the problem
from manim import *
from manim.typing import *

class testCirclePointAtAngle(Scene):

    def construct(self):
        # Flip circle so that it starts on LEFT, then rotate 1/4 turn clockwise
        # At the end of this, the start of the circle is at UP, and it proceeds clockwise from there
        bad_circle = (
            Circle(radius=2, stroke_color=RED)
            .flip()
            .rotate(1 / 4 * -TAU)
            .move_to(LEFT * 3)
        )
        bad_text = Text(
            "using Circle.point_at_angle()", color=RED, font_size=24
        ).next_to(bad_circle, DOWN)
        good_circle = (
            Circle(radius=2, stroke_color=GREEN)
            .flip()
            .rotate(1 / 4 * -TAU)
            .move_to(RIGHT * 3)
        )
        good_text = Text("fixed version", color=GREEN, font_size=24).next_to(
            good_circle, DOWN
        )
        self.play(
            Create(bad_circle), Create(bad_text), Create(good_circle), Create(good_text)
        )
        self.wait(1)

        def fixed_point_at_angle(circle: Circle, angle: float) -> Point3D:
            proportion = (angle) / TAU
            proportion -= np.floor(proportion)
            return circle.point_from_proportion(proportion)

        # Create 12 white dots, spaced evenly around the circle, STARTING where the circle starts.
        for i in range(12):
            # BUG: These dots start at LEFT instead of UP.
            # This is because of the logic in point_at_angle:
            # - start_angle is computed by projecting circle.points[0] onto the XY plane,
            #   then reading its angle via angle_of_vector() .. as if the circle was still in "mathematical" orientation.
            #   here, `start_angle` takes the value 1.570796..., i.e. 90° counterclockwise from mathemetical 0° rotation.
            # - proportion is then computed as (angle - start_angle) / TAU.
            #   but param `angle` is "The angle of the point along the circle in radians." Doesn't that mean "from self.points[0]"?
            #   shouldn't `angle=0` return a point at `self.points[0]`?
            #   instead proportion takes (0 - 1.570796...) / TAU = -0.75, then the cycling with `np.floor()` brings it to 0.25.
            # TODO: fix for this should be to just remove start_angle in point_at_angle(). calculate `proportion` as `angle / TAU`.
            self.add(
                Point(
                    bad_circle.point_at_angle(TAU * i / 12),
                    color=WHITE,
                    stroke_width=10,
                )
            )
            self.add(
                Point(
                    fixed_point_at_angle(good_circle, TAU * i / 12),
                    color=WHITE,
                    stroke_width=10,
                )
            )
            self.wait(0.3333333)

        self.wait(1)

Additional media files

Images/GIFs
testCirclePointAtAngle.mp4

Logs

Terminal output
❯ "/mnt/c/Users/pikab/Programming/Personal/harmonimation/.venv/bin/manim" "/mnt/c/Users/pikab/Programming/Personal/harmonimation/renderer/play.py" testCirclePointAtAngle --disable_caching -v DEBUG
Manim Community v0.19.0

[05/04/25 04:19:27] INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000']                                                                                                                                         cairo_renderer.py:98
[05/04/25 04:19:30] INFO     Animation 0 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00000.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001']                                                                                                                       cairo_renderer.py:98
[05/04/25 04:19:32] INFO     Animation 1 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00001.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002']                                                                                                     cairo_renderer.py:98
[05/04/25 04:19:33] INFO     Animation 2 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00002.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003']                                                                                   cairo_renderer.py:98
                    INFO     Animation 3 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00003.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:34] INFO     Animation 4 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00004.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
                    INFO     Animation 5 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00005.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:35] INFO     Animation 6 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00006.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
                    INFO     Animation 7 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00007.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:36] INFO     Animation 8 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00008.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
                    INFO     Animation 9 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00009.mp4'                 scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:37] INFO     Animation 10 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00010.mp4'                scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
                    INFO     Animation 11 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00011.mp4'                scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:38] INFO     Animation 12 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00012.mp4'                scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
                    INFO     Animation 13 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00013.mp4'                scene_file_writer.py:588
                    DEBUG    Animation with empty mobject                                                                                                                                                                                        animation.py:190
                    INFO     Caching disabled.                                                                                                                                                                                               cairo_renderer.py:79
                    DEBUG    List of the first few animation hashes of the scene: ['uncached_00000', 'uncached_00001', 'uncached_00002', 'uncached_00003', 'uncached_00004']                                                                 cairo_renderer.py:98
[05/04/25 04:19:39] INFO     Animation 14 : Partial movie file written in '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00014.mp4'                scene_file_writer.py:588
                    INFO     Combining to Movie file.                                                                                                                                                                                    scene_file_writer.py:739
                    DEBUG    Partial movie files to combine (15 files): ['/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00000.mp4',                scene_file_writer.py:622
                             '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00001.mp4',                                                                                    
                             '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00002.mp4',                                                                                    
                             '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00003.mp4',                                                                                    
                             '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/partial_movie_files/testCirclePointAtAngle/uncached_00004.mp4']                                                                                    
[05/04/25 04:19:40] INFO                                                                                                                                                                                                                 scene_file_writer.py:886
                             File ready at '/mnt/c/Users/pikab/Programming/Personal/harmonimation/media/videos/play/1080p60/testCirclePointAtAngle.mp4'                                                                                                          
                                                                                                                                                                                                                                                                 
                    INFO     Rendered testCirclePointAtAngle                                                                                                                                                                                         scene.py:255
                             Played 15 animations   

System specifications

System Details
  • OS: Windows 11 Version 10.0.22631 Build 22631
  • RAM: 48.0 GB
  • Python version (python/py/python3 --version): 3.13.2
  • Installed modules (provide output from pip list):
Package            Version
------------------ -----------
audioop-lts        0.2.1
av                 13.1.0
beautifulsoup4     4.13.3
black              25.1.0
certifi            2025.1.31
chardet            5.2.0
charset-normalizer 3.4.1
click              8.1.8
cloup              3.0.7
contourpy          1.3.1
cycler             0.12.1
decorator          5.2.1
fonttools          4.56.0
glcontext          3.0.0
idna               3.10
isosurfaces        0.1.2
joblib             1.4.2
jsonpickle         4.0.5
kiwisolver         1.4.8
manim              0.19.0
ManimPango         0.6.0
mapbox_earcut      1.0.3
markdown-it-py     3.0.0
matplotlib         3.10.1
mdurl              0.1.2
moderngl           5.12.0
moderngl-window    3.1.1
more-itertools     10.6.0
music21            9.3.0
mypy-extensions    1.0.0
networkx           3.4.2
numpy              2.2.4
packaging          24.2
pathspec           0.12.1
pillow             11.1.0
pip                24.3.1
platformdirs       4.3.7
pycairo            1.27.0
pydub              0.25.1
pyglet             2.1.3
pyglm              2.8.1
Pygments           2.19.1
pyjson5            1.6.8
pyparsing          3.2.3
python-dateutil    2.9.0.post0
regex              2024.11.6
requests           2.32.3
rich               13.9.4
scipy              1.15.2
screeninfo         0.8.1
six                1.17.0
skia-pathops       0.8.0.post2
soupsieve          2.6
srt                3.5.3
svgelements        1.9.6
tqdm               4.67.1
typing_extensions  4.13.0
urllib3            2.3.0
watchdog           6.0.0
webcolors          24.11.1
LaTeX details
  • LaTeX distribution: TeX Live 2025
  • Installed LaTeX packages: (a giant list, I can include them if you'd like, but it's a full install)

Additional comments

This should be the body of the corrected Circle.point_at_angle():

    def point_at_angle(self, angle: float) -> Point3D:
        """..."""
        proportion = angle / TAU
        proportion -= np.floor(proportion)
        return self.point_from_proportion(proportion)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions