Skip to content

Instantly share code, notes, and snippets.

@uwezi
Created January 28, 2026 17:41
Show Gist options
  • Select an option

  • Save uwezi/80eef4677fb1526bed672ad43e8383f4 to your computer and use it in GitHub Desktop.

Select an option

Save uwezi/80eef4677fb1526bed672ad43e8383f4 to your computer and use it in GitHub Desktop.
[Analog meter] A class object for analog meters. #manim #animation #electronics #class
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment