| from manim import* | |
| class AnalogMeter(VGroup): | |
| def __init__(self, | |
| value = 0, | |
| width=4, | |
| height=3.5, | |
| corner_radius=0.5, | |
| outer_stroke_width=5, | |
| outer_stroke_color=GRAY, | |
| frame_color=BLACK, | |
| scale_aspect=4/2.5, | |
| scale_background_color=0xffffcc, | |
| scale_color=0x000099, | |
| scale_range=[0,10,2], | |
| scale_mtics=5, | |
| scale_format="{:.0f}", | |
| scale_fontsize=18, | |
| scale_unit="mA", | |
| scale_unit_fontsize=28, | |
| scale_unit_pos=(0.5,0.4), | |
| pointer_color=BLACK, | |
| pointer_stroke_width=3, | |
| frame_width=0.2, | |
| **kwargs | |
| ): | |
| self.frame = RoundedRectangle( | |
| width=width, height=height, | |
| corner_radius=corner_radius, | |
| ) | |
| self.scale_background = RoundedRectangle( | |
| width=width-2*frame_width, height=(width-2*frame_width)/scale_aspect, | |
| corner_radius=corner_radius-frame_width if frame_width<corner_radius else 0.1, | |
| fill_opacity=1.0, fill_color=scale_background_color, | |
| stroke_width=0, | |
| ).set_z_index(0) | |
| self.scale_background.shift(self.frame.get_top()-self.scale_background.get_top()+frame_width*DOWN) | |
| self.frame = Difference( | |
| self.frame, | |
| self.scale_background | |
| ).set_stroke( | |
| width=outer_stroke_width, color=outer_stroke_color, | |
| ).set_fill( | |
| opacity=1.0, color=frame_color | |
| ).set_z_index(2) | |
| self.pivot = VGroup( | |
| Circle( | |
| radius=frame_width/2, | |
| stroke_width=outer_stroke_width/2, color=outer_stroke_color, | |
| ), | |
| Line( | |
| frame_width/2*LEFT, | |
| frame_width/2*RIGHT, | |
| stroke_width=outer_stroke_width, | |
| ).rotate(30*DEGREES) | |
| ).set_z_index(3) | |
| self.pivot.shift((self.frame.get_bottom()+self.scale_background.get_bottom())/2-self.pivot[0].get_center()) | |
| self.value = ValueTracker(value) | |
| self.pointer = Line( | |
| ORIGIN,1.3*0.5*self.scale_background.width*RIGHT, | |
| color = pointer_color, | |
| stroke_width=pointer_stroke_width, | |
| ).shift( | |
| self.pivot[0].get_center() | |
| ).rotate( | |
| 135*DEGREES, about_point=self.pivot[0].get_center() | |
| ).set_z_index(1) | |
| self.scale_values = np.arange(start=scale_range[0],stop=scale_range[1]+scale_range[2]/2,step=scale_range[2]) | |
| self.scale_arc = VGroup( | |
| Arc( | |
| radius=0.9*self.pointer.get_length(), | |
| start_angle=45*DEGREES, | |
| angle=90*DEGREES, | |
| arc_center=self.pivot.get_center(), | |
| stroke_width=3, | |
| stroke_color=scale_color, | |
| ), | |
| *[ | |
| VGroup( | |
| Line( | |
| self.pointer.point_from_proportion(0.85), | |
| self.pointer.point_from_proportion(0.95), | |
| stroke_width=3, | |
| stroke_color=scale_color, | |
| ), | |
| MathTex( | |
| scale_format.format(value), | |
| font_size=scale_fontsize, | |
| color=scale_color, | |
| ).rotate(45*DEGREES).shift(self.pointer.point_from_proportion(1)) | |
| ).rotate(-i*90*DEGREES/(len(self.scale_values)-1), about_point=self.pivot.get_center()) | |
| for i,value in enumerate(self.scale_values) | |
| ], | |
| *[ | |
| VGroup( | |
| Line( | |
| self.pointer.point_from_proportion(0.90), | |
| self.pointer.point_from_proportion(0.95), | |
| stroke_width=1, | |
| stroke_color=scale_color, | |
| ), | |
| ).rotate(-(i+j/scale_mtics)*90*DEGREES/(len(self.scale_values)-1), about_point=self.pivot.get_center()) | |
| for i in range(len(self.scale_values)-1) for j in range(scale_mtics) | |
| ], | |
| Tex( | |
| scale_unit, font_size=scale_unit_fontsize,color=scale_color | |
| ).shift( | |
| [ | |
| self.scale_background.get_left()[0]+scale_unit_pos[0]*self.scale_background.width, | |
| self.scale_background.get_bottom()[1]+scale_unit_pos[1]*self.scale_background.height, | |
| 0 | |
| ] | |
| ), | |
| ).set_z_index(1) | |
| super().__init__( | |
| self.frame, | |
| self.scale_background, | |
| self.pivot, | |
| self.pointer, | |
| self.scale_arc, | |
| **kwargs | |
| ) | |
| self.add_updater(self.updater, call_updater=True) | |
| def updater(self, mobj): | |
| newangle = 135*DEGREES-90*DEGREES*self.value.get_value() | |
| self.pointer.rotate( | |
| angle=newangle-self.pointer.get_angle(), | |
| about_point=self.pivot[0].get_center() | |
| ) | |
| def scale(self, scale_factor, scale_stroke = True, **kwargs): | |
| return super().scale(scale_factor=scale_factor, scale_stroke=scale_stroke, **kwargs) | |
| class metertest(Scene): | |
| def construct(self): | |
| ampmeter = AnalogMeter(scale_color=0x003399, scale_unit="µA", scale_range=[0,100,20]) | |
| voltmeter = AnalogMeter(scale_color=0xb30000, scale_unit="V").next_to(ampmeter,RIGHT) | |
| self.add(ampmeter,voltmeter) | |
| for _ in range(5): | |
| self.play( | |
| ampmeter.value.animate.set_value(np.random.uniform(0,1)), | |
| voltmeter.value.animate.set_value(np.random.uniform(0,1)), | |
| ) | |
| self.wait() |