44
55class FourierCirclesScene (Scene ):
66 CONFIG = {
7- "n_circles " : 10 ,
7+ "n_vectors " : 10 ,
88 "big_radius" : 2 ,
99 "colors" : [
1010 BLUE_D ,
@@ -15,14 +15,16 @@ class FourierCirclesScene(Scene):
1515 "circle_style" : {
1616 "stroke_width" : 2 ,
1717 },
18- "arrow_config " : {
18+ "vector_config " : {
1919 "buff" : 0 ,
2020 "max_tip_length_to_length_ratio" : 0.35 ,
2121 "tip_length" : 0.15 ,
2222 "max_stroke_width_to_length_ratio" : 10 ,
2323 "stroke_width" : 2 ,
2424 },
25- "use_vectors" : True ,
25+ "circle_config" : {
26+ "stroke_width" : 1 ,
27+ },
2628 "base_frequency" : 1 ,
2729 "slow_factor" : 0.25 ,
2830 "center_point" : ORIGIN ,
@@ -39,101 +41,90 @@ def get_slow_factor(self):
3941
4042 #
4143 def get_freqs (self ):
42- n = self .n_circles
44+ n = self .n_vectors
4345 all_freqs = list (range (n // 2 , - n // 2 , - 1 ))
4446 all_freqs .sort (key = abs )
4547 return all_freqs
4648
4749 def get_coefficients (self ):
48- return [complex (0 ) for x in range (self .n_circles )]
50+ return [complex (0 ) for x in range (self .n_vectors )]
4951
5052 def get_color_iterator (self ):
5153 return it .cycle (self .colors )
5254
53- def get_circles (self , freqs = None , coefficients = None ):
54- circles = VGroup ()
55- color_iterator = self .get_color_iterator ()
55+ def get_rotating_vectors (self , freqs = None , coefficients = None ):
56+ vectors = VGroup ()
5657 self .center_tracker = VectorizedPoint (self .center_point )
5758
5859 if freqs is None :
5960 freqs = self .get_freqs ()
6061 if coefficients is None :
6162 coefficients = self .get_coefficients ()
6263
63- last_circle = None
64+ last_vector = None
6465 for freq , coefficient in zip (freqs , coefficients ):
65- if last_circle :
66- center_func = last_circle . get_start
66+ if last_vector :
67+ center_func = last_vector . get_end
6768 else :
6869 center_func = self .center_tracker .get_location
69- circle = self .get_circle (
70+ vector = self .get_rotating_vector (
7071 coefficient = coefficient ,
7172 freq = freq ,
72- color = next (color_iterator ),
7373 center_func = center_func ,
7474 )
75- circles .add (circle )
76- last_circle = circle
77- return circles
75+ vectors .add (vector )
76+ last_vector = vector
77+ return vectors
7878
79- def get_circle (self , coefficient , freq , color , center_func ):
80- radius = abs (coefficient )
79+ def get_rotating_vector (self , coefficient , freq , center_func ):
80+ vector = Vector (RIGHT , ** self .vector_config )
81+ vector .scale (abs (coefficient ))
8182 phase = np .log (coefficient ).imag
82- circle = Circle (
83- radius = radius ,
84- color = color ,
85- ** self .circle_style ,
83+ vector .rotate (phase , about_point = ORIGIN )
84+ vector .freq = freq
85+ vector .phase = phase
86+ vector .coefficient = coefficient
87+ vector .center_func = center_func
88+ vector .add_updater (self .update_vector )
89+ return vector
90+
91+ def update_vector (self , vector , dt ):
92+ vector .rotate (
93+ self .get_slow_factor () * vector .freq * dt * TAU
8694 )
87- line_points = (
88- circle .get_center (),
89- circle .get_start (),
95+ vector .shift (
96+ vector .center_func () - vector .get_start ()
9097 )
91- if self .use_vectors :
92- circle .radial_line = Arrow (
93- * line_points ,
94- ** self .arrow_config ,
98+ return vector
99+
100+ def get_circles (self , vectors ):
101+ return VGroup (* [
102+ self .get_circle (
103+ vector ,
104+ color = color
95105 )
96- else :
97- circle .radial_line = Line (
98- * line_points ,
99- color = WHITE ,
100- ** self .circle_style ,
106+ for vector , color in zip (
107+ vectors ,
108+ self .get_color_iterator ()
101109 )
102- circle . add ( circle . radial_line )
103- circle . freq = freq
104- circle . phase = phase
105- circle . rotate ( phase )
106- circle .coefficient = coefficient
107- circle . center_func = center_func
110+ ] )
111+
112+ def get_circle ( self , vector , color = BLUE ):
113+ circle = Circle ( color = color , ** self . circle_config )
114+ circle .vector = vector
115+ vector . circle = circle
108116 circle .add_updater (self .update_circle )
109117 return circle
110118
111- def update_circle (self , circle , dt ):
112- circle .rotate (
113- self .get_slow_factor () * circle .freq * dt * TAU
114- )
115- circle .move_to (circle .center_func ())
119+ def update_circle (self , circle ):
120+ circle .set_width (2 * circle .vector .get_length ())
121+ circle .move_to (circle .vector .get_start ())
116122 return circle
117123
118- def get_rotating_vectors (self , circles ):
119- return VGroup (* [
120- self .get_rotating_vector (circle )
121- for circle in circles
122- ])
123-
124- def get_rotating_vector (self , circle ):
125- vector = Vector (RIGHT , color = WHITE )
126- vector .add_updater (lambda v , dt : v .put_start_and_end_on (
127- circle .get_center (),
128- circle .get_start (),
129- ))
130- circle .vector = vector
131- return vector
132-
133- def get_circle_end_path (self , circles , color = YELLOW ):
134- coefs = [c .coefficient for c in circles ]
135- freqs = [c .freq for c in circles ]
136- center = circles [0 ].get_center ()
124+ def get_vector_sum_path (self , vectors , color = YELLOW ):
125+ coefs = [v .coefficient for v in vectors ]
126+ freqs = [v .freq for v in vectors ]
127+ center = vectors [0 ].get_start ()
137128
138129 path = ParametricFunction (
139130 lambda t : center + reduce (op .add , [
@@ -150,8 +141,8 @@ def get_circle_end_path(self, circles, color=YELLOW):
150141 return path
151142
152143 # TODO, this should be a general animated mobect
153- def get_drawn_path (self , circles , stroke_width = 2 , ** kwargs ):
154- path = self .get_circle_end_path ( circles , ** kwargs )
144+ def get_drawn_path (self , vectors , stroke_width = 2 , ** kwargs ):
145+ path = self .get_vector_sum_path ( vectors , ** kwargs )
155146 broken_path = CurvesAsSubmobjects (path )
156147 broken_path .curr_time = 0
157148
@@ -173,12 +164,12 @@ def update_path(path, dt):
173164 return broken_path
174165
175166 def get_y_component_wave (self ,
176- circles ,
167+ vectors ,
177168 left_x = 1 ,
178169 color = PINK ,
179170 n_copies = 2 ,
180171 right_shift_rate = 5 ):
181- path = self .get_circle_end_path ( circles )
172+ path = self .get_vector_sum_path ( vectors )
182173 wave = ParametricFunction (
183174 lambda t : op .add (
184175 right_shift_rate * t * LEFT ,
@@ -216,15 +207,16 @@ def update_wave_copies(wcs):
216207
217208 return VGroup (wave , wave_copies )
218209
219- def get_wave_y_line (self , circles , wave ):
210+ def get_wave_y_line (self , vectors , wave ):
220211 return DashedLine (
221- circles [- 1 ].get_start (),
212+ vectors [- 1 ].get_end (),
222213 wave [0 ].get_end (),
223214 stroke_width = 1 ,
224215 dash_length = DEFAULT_DASH_LENGTH * 0.5 ,
225216 )
226217
227218 # Computing Fourier series
219+ # i.e. where all the math happens
228220 def get_coefficients_of_path (self , path , n_samples = 10000 , freqs = None ):
229221 if freqs is None :
230222 freqs = self .get_freqs ()
@@ -250,7 +242,7 @@ def get_coefficients_of_path(self, path, n_samples=10000, freqs=None):
250242
251243class FourierSeriesIntroBackground4 (FourierCirclesScene ):
252244 CONFIG = {
253- "n_circles " : 4 ,
245+ "n_vectors " : 4 ,
254246 "center_point" : 4 * LEFT ,
255247 "run_time" : 30 ,
256248 "big_radius" : 1.5 ,
@@ -271,7 +263,7 @@ def construct(self):
271263 self .wait (self .run_time )
272264
273265 def get_ks (self ):
274- return np .arange (1 , 2 * self .n_circles + 1 , 2 )
266+ return np .arange (1 , 2 * self .n_vectors + 1 , 2 )
275267
276268 def get_freqs (self ):
277269 return self .base_frequency * self .get_ks ()
@@ -282,25 +274,25 @@ def get_coefficients(self):
282274
283275class FourierSeriesIntroBackground8 (FourierSeriesIntroBackground4 ):
284276 CONFIG = {
285- "n_circles " : 8 ,
277+ "n_vectors " : 8 ,
286278 }
287279
288280
289281class FourierSeriesIntroBackground12 (FourierSeriesIntroBackground4 ):
290282 CONFIG = {
291- "n_circles " : 12 ,
283+ "n_vectors " : 12 ,
292284 }
293285
294286
295287class FourierSeriesIntroBackground20 (FourierSeriesIntroBackground4 ):
296288 CONFIG = {
297- "n_circles " : 20 ,
289+ "n_vectors " : 20 ,
298290 }
299291
300292
301293class FourierOfPiSymbol (FourierCirclesScene ):
302294 CONFIG = {
303- "n_circles " : 50 ,
295+ "n_vectors " : 51 ,
304296 "center_point" : ORIGIN ,
305297 "slow_factor" : 0.1 ,
306298 "run_time" : 30 ,
@@ -312,14 +304,16 @@ def construct(self):
312304 path = self .get_path ()
313305 coefs = self .get_coefficients_of_path (path )
314306
315- circles = self .get_circles (coefficients = coefs )
307+ vectors = self .get_rotating_vectors (coefficients = coefs )
308+ circles = self .get_circles (vectors )
316309 self .set_decreasing_stroke_widths (circles )
317- # approx_path = self.get_circle_end_path (circles)
318- drawn_path = self .get_drawn_path (circles )
310+ # approx_path = self.get_vector_sum_path (circles)
311+ drawn_path = self .get_drawn_path (vectors )
319312 if self .start_drawn :
320313 drawn_path .curr_time = 1 / self .slow_factor
321314
322315 self .add (path )
316+ self .add (vectors )
323317 self .add (circles )
324318 self .add (drawn_path )
325319 self .wait (self .run_time )
@@ -343,7 +337,7 @@ def get_path(self):
343337
344338class FourierOfName (FourierOfPiSymbol ):
345339 CONFIG = {
346- "n_circles " : 100 ,
340+ "n_vectors " : 100 ,
347341 "name_color" : WHITE ,
348342 "name_text" : "Abc" ,
349343 "time_per_symbol" : 5 ,
@@ -388,14 +382,14 @@ def construct(self):
388382
389383class FourierOfPiSymbol5 (FourierOfPiSymbol ):
390384 CONFIG = {
391- "n_circles " : 5 ,
385+ "n_vectors " : 5 ,
392386 "run_time" : 10 ,
393387 }
394388
395389
396390class FourierOfTrebleClef (FourierOfPiSymbol ):
397391 CONFIG = {
398- "n_circles " : 100 ,
392+ "n_vectors " : 101 ,
399393 "run_time" : 10 ,
400394 "start_drawn" : True ,
401395 "file_name" : "TrebleClef" ,
@@ -419,7 +413,7 @@ class FourierOfIP(FourierOfTrebleClef):
419413 CONFIG = {
420414 "file_name" : "IP_logo2" ,
421415 "height" : 6 ,
422- "n_circles " : 100 ,
416+ "n_vectors " : 100 ,
423417 }
424418
425419 # def construct(self):
@@ -451,7 +445,7 @@ class FourierOfEighthNote(FourierOfTrebleClef):
451445class FourierOfN (FourierOfTrebleClef ):
452446 CONFIG = {
453447 "height" : 6 ,
454- "n_circles " : 1000 ,
448+ "n_vectors " : 1000 ,
455449 }
456450
457451 def get_shape (self ):
@@ -461,7 +455,7 @@ def get_shape(self):
461455class FourierNailAndGear (FourierOfTrebleClef ):
462456 CONFIG = {
463457 "height" : 6 ,
464- "n_circles " : 200 ,
458+ "n_vectors " : 200 ,
465459 "run_time" : 100 ,
466460 "slow_factor" : 0.01 ,
467461 "parametric_function_step_size" : 0.0001 ,
@@ -479,7 +473,7 @@ def get_shape(self):
479473class FourierBatman (FourierOfTrebleClef ):
480474 CONFIG = {
481475 "height" : 4 ,
482- "n_circles " : 100 ,
476+ "n_vectors " : 100 ,
483477 "run_time" : 10 ,
484478 "arrow_config" : {
485479 "tip_length" : 0.1 ,
@@ -495,7 +489,7 @@ def get_shape(self):
495489class FourierHeart (FourierOfTrebleClef ):
496490 CONFIG = {
497491 "height" : 4 ,
498- "n_circles " : 100 ,
492+ "n_vectors " : 100 ,
499493 "run_time" : 10 ,
500494 "arrow_config" : {
501495 "tip_length" : 0.1 ,
@@ -517,7 +511,7 @@ def get_drawn_path(self, *args, **kwargs):
517511class FourierNDQ (FourierOfTrebleClef ):
518512 CONFIG = {
519513 "height" : 4 ,
520- "n_circles " : 1000 ,
514+ "n_vectors " : 1000 ,
521515 "run_time" : 10 ,
522516 "arrow_config" : {
523517 "tip_length" : 0.1 ,
@@ -535,7 +529,7 @@ def get_shape(self):
535529
536530class FourierGoogleG (FourierOfTrebleClef ):
537531 CONFIG = {
538- "n_circles " : 10 ,
532+ "n_vectors " : 10 ,
539533 "height" : 5 ,
540534 "g_colors" : [
541535 "#4285F4" ,
@@ -570,7 +564,7 @@ def get_drawn_path(self, *args, **kwargs):
570564
571565class ExplainCircleAnimations (FourierCirclesScene ):
572566 CONFIG = {
573- "n_circles " : 100 ,
567+ "n_vectors " : 100 ,
574568 "center_point" : 2 * DOWN ,
575569 "n_top_circles" : 9 ,
576570 "path_height" : 3 ,
0 commit comments