__all__ = [
'CompassScene'
]
# from manim import *
from typing import List
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.arc import Arc
from manim.mobject.types.point_cloud_mobject import Point
from manim.animation.animation import Animation
from manim.animation.composition import AnimationGroup
from manim.animation.rotation import Rotate
from manim.animation.creation import Create
from manim.scene.moving_camera_scene import MovingCameraScene
from manim.utils.rate_functions import linear
from manim.utils.color.manim_colors import YELLOW
from manim.constants import *
from ..compass import Compass,Ruler,Pencil
from ..animations import *
from ..utils.geometry_method import (
get_distance,
get_vecs_angle
)
[docs]
class CompassScene(MovingCameraScene):
'''
A scene equipped with a compass, ruler, and pencil. Mainly implements
compass placement, arc drawing, and ruler/pencil animations.
'''
[docs]
def setup(self):
'''CompassScene.setup example.
'''
self.compass = Compass(span = 0.5).to_edge(LEFT)
self.ruler = Ruler().to_edge(DOWN)
self.pencil = Pencil().to_corner(UR)
[docs]
def compass_move_niddle_tip_to(self,pos = ORIGIN,run_time = 1):
'''Move the compass needle tip to pos.
'''
self.play(
self.compass.animate.move_niddle_tip_to(pos),
# MoveNiddleTipTo(self.compass,pos),
run_time = run_time
)
[docs]
def rotate_compass_about_niddle_tip(
self,
angle_or_arc:float | Arc,
arc:Arc = None,
added_anims:List[Animation] = None,
**kwargs
):
'''
Rotate angle around the compass needle tip (niddle_tip).
'''
anims = [
Rotate(
self.compass,
about_point = self.compass.get_niddle_tip(),
angle = angle_or_arc.angle if isinstance(angle_or_arc,Arc) else angle_or_arc
),
# RotateCompass(self.compass,angle = angle_or_arc.angle if isinstance(angle_or_arc,Arc) else angle_or_arc)
]
if added_anims is not None:
anims.extend(added_anims)
if isinstance(angle_or_arc,Arc):
anims.append(Create(angle_or_arc))
self.play(*anims, **kwargs)
[docs]
def compass_split_span(self,span = 3,run_time = 1):
'''Rotate the two compass legs uniformly outward/inward so the opened distance equals span.
'''
self.play(
SplitCompass(self.compass,span = span),
run_time = run_time,
rate_func = linear
)
[docs]
def split_cmpass_span(self,span = 1,run_time = 1):
'''
Fix niddle_tip, then move pen_tip along the line through niddle_tip and pen_tip to reach the given span.
'''
angle = self.compass.get_compass_rotate_angle_with_span(span)
self.play(
self.compass.animate.split_compass_with_niddle_tip_fixed(
angle/2,
self.compass.get_niddle_tip()
),
run_time = run_time
)
[docs]
def set_compass(
self,
niddle_pos:Point = None,
pen_pos:Point = None,
run_time:float = 1.0
):
'''
Place the compass at the specified positions: move niddle_tip to niddle_pos and pen_tip to pen_pos.
Parameters
----------
niddle_pos : Point
target position for the compass needle tip (niddle_tip)
pen_pos : Point
target position for the compass pen tip (pen_tip)
'''
self.play(
PutCompass(
self.compass,
niddle_pos = niddle_pos,
pen_pos = pen_pos,
),
run_time = run_time
)
[docs]
def draw_arc(
self,
niddle_point = ORIGIN,
pen_point = RIGHT,
angle = PI/3,
move_time = 1.0,
wait_time = 1.0,
run_time = 1.0,
arc_color = None,
**kwargs
)-> Arc:
'''
Draw an arc with the compass. The arc radius is computed from niddle_point and pen_point.
Parameters
----------
niddle_point
centre of the arc
pen_point
starting point of the arc
angle
central angle of the arc
move_time
time to move the compass into position
run_time
time to draw the arc
wait_time
wait time between the two animations
arc_color
colour of the arc
kwargs
other keyword arguments for the arc
return
The drawn arc
'''
self.set_compass(
niddle_point,
pen_point,
run_time = move_time
)
if wait_time > 0:
self.wait(wait_time)
arc_radius = get_distance(niddle_point,pen_point)
arc = Arc(
arc_center = niddle_point,
radius = arc_radius,
start_angle = get_vecs_angle(RIGHT,self.compass.get_niddle2pen_vec()),
angle = angle,
color = self.compass.pen_tip.get_color() if arc_color is None else arc_color,
**kwargs
)
self.play(
DrawArc(
self.compass,
arc,
),
run_time = run_time
)
return arc
[docs]
def flip_compass(self,run_time = 1):
'''Flip the compass.
'''
self.play(
self.compass.animate.reverse_tip(),
run_time = run_time
)
[docs]
def put_compass_aside(
self,
aside_pos:Point = RIGHT,
span_buff:float = 0.1,
run_time:float = 1.0
):
'''
Put the compass aside.
Parameters
----------
aside_pos : Point
position to place the compass
span_buff : float
distance between the two compass tips when placed aside
run_time : float
time required to place the compass
'''
r = 0.5*self.compass.leg_length
vec = r*DOWN if self.compass.get_compass_rotate_angle_direction() else r*UP
self.set_compass(
niddle_pos = aside_pos + span_buff*LEFT + vec ,
pen_pos = aside_pos + span_buff*RIGHT + vec,
run_time = run_time
)
[docs]
def set_ruler(
self,
start:Point = None,
end:Point = None,
lag_ratio:float = 0.5,
run_time:float = 1.0,
with_pencil:bool = True
):
'''
Place the ruler so that one of its edges aligns with start and end.
Parameters
----------
start : Point
start point of the ruler placement
end : Point
end point of the ruler placement
lag_ratio : float
lag ratio between the ruler and pencil placement animations
run_time : float
time to place the ruler
with_pencil : bool
whether to place the pencil at the same time
'''
if with_pencil:
self.play(
AnimationGroup(
PutRuler(self.ruler,start = start,end = end),
MovePencilTipTo(self.pencil,start),
lag_ratio = lag_ratio
),
run_time = run_time
)
else:
self.play(
PutRuler(self.ruler,start = start,end = end),
run_time = run_time
)
[docs]
def set_pencil(self,pos,run_time = 1.0):
'''Move the pencil nib to the specified position.
'''
self.play(
self.pencil.animate.move_nid_to(pos),
# MovePencilTipTo(self.pencil,pos),
run_time = run_time
)
[docs]
def draw_line(
self,
start:Point = None,
end:Point = None,
run_time:float = 1.0,
with_pencil:bool = True,
color = YELLOW,
**kwargs
)-> Line:
'''Draw a straight line using the ruler.
'''
self.set_ruler(start = start,end = end,run_time = 0.5*run_time,with_pencil = with_pencil)
line = Line(start,end,color = color,**kwargs)
if with_pencil:
self.play(
DrawPath(self.pencil,line),
run_time = 0.5*run_time
)
else:
self.play(
Create(line),
run_time = 0.5*run_time
)
return line
[docs]
def put_pencil_away(self,pos = 3*DOWN,run_time = 1):
'''Translate the pencil as a whole to the specified position.
'''
curr_pos = self.pencil.get_center()
self.play(
self.pencil.animate.shift(pos - curr_pos),
run_time = run_time
)
[docs]
def put_ruler_aside(
self,
aside_pos:Point = 3*DOWN,
horizontal_or_vertical:bool = True,
run_time:float = 1.0
):
'''
Put the ruler aside at aside_pos.
Parameters
----------
aside_pos : Point
position where the ruler will be placed
horizontal_or_vertical : bool
whether to place it horizontally
run_time : float
time required to place the ruler
'''
vec_w = self.ruler.get_direction_vector_of_ruler()
vec = RIGHT if horizontal_or_vertical else DOWN
self.play(
Rotate(
self.ruler,
about_point = self.ruler.get_center(),
angle = get_vecs_angle(vec_w,vec)
),
run_time = 0.35*run_time
)
self.play(
self.ruler.animate.move_to(aside_pos),
run_time = 0.65*run_time
)