manim-derivative-plotter 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: manim-derivative_plotter
3
+ Version: 1.0.0
4
+ Summary: a simple tool for plotting polynomial derivatives with manim
5
+ Author-email: TyrannoJ <julius.megerle@outlook.de>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/TyrannoJ/manim
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: manim
14
+ Requires-Dist: sympy
15
+ Requires-Dist: typst
16
+ Provides-Extra: typst
17
+ Requires-Dist: manim[typst]; extra == "typst"
18
+ Dynamic: license-file
19
+
20
+ # This is a Python Derivative plotter made with manim
21
+
22
+ ## What does it do?
23
+
24
+ There are two main codes here.
25
+ - useful_derivatives.py
26
+ - derivatives_simple.py
27
+
28
+ With both you can basically do the same
29
+ 1. Give it a simple polynomial function as a String like "2x4+3x2"
30
+ 2. It calculates the first and second derivatives of the function
31
+ 3. It plots all of them
32
+ 4. It shows you all their zero points
33
+ 5. It shows you the Extremes and Turning points of the function
34
+
35
+ The only difference between the two is, that the simple version only shows all that to you while the other one animates it
36
+ [look at the program outputs here](https://tyrannoj.github.io/manim/index.html)
37
+
38
+ ## What do the results mean:
39
+
40
+ ### in the picture version
41
+ - blue line: main function
42
+ - blue dots: zeros of the main function
43
+ - green line: the first derivative function
44
+ - green dots: extremes of the main function (TIP=local minimum,HOP=local maxiumum,TEP=middle points with zero slope)
45
+ - red line: second derivative function
46
+ - red dots: turning points of the main function
47
+ - purple is where the main function is curved to the left
48
+ - yellow is where the main function is curved to the right
49
+
50
+ ### in the animation
51
+
52
+ - First it animates the main function and it's zeros in blue
53
+ - then the first derivative function and it's zeros in green
54
+ - then the second derivative function and it's zeros in red
55
+ - Afterwards it shows you the extremes in Blue (TIP=local minimum,HOP=local maxiumum,TEP=middle points with zero slope)
56
+ - And then the turns in Blue and the curvature of the function purple=left yellow=right and the turning points
57
+
58
+ ## How can you do it yourself
59
+
60
+
61
+ - You can simply run
62
+ ```pip install manim-derivatives-plotter ```
63
+ - Then you can run
64
+ ```python -c "from manim_derivatives import render_picture; render_picture()"``` for an image
65
+ - and
66
+ ```python -c "from manim_derivatives import render_animation; render_animation()" ```for a video.
67
+ - to Input your function you just type it in the command prompt
68
+ - Input style for functions: *coefficient*x*power*+*next-coefficien*x*next-power*...
69
+ - **A few examples:**
70
+ 1. 2x3
71
+ 2. x3-4x2
72
+ 3. 4.3x4+x
73
+ 4. -2.5x6-x2+3
74
+
75
+ ## How did I build it
76
+ I used *manim* to animate and *sympy* for the zero points, so a big thanks to Grant SAnderson from 3blue1brown. I used perplexity and a little GitHub Copilot for research especially for specific manim functions and to write a few little functionalities with syntaxes I didn't know but never more than 2-3 lines and a lot of help on the package structure. I also used it to understand the errors that were output.
77
+ This is my first manim project and I'm quite happy with how it turned out and I plan to do more.
78
+
79
+
80
+
81
+ ## to Do (for future reference)
82
+ - maybe make interactive
83
+ - make input more user friendly
84
+ - make handle of constants better
85
+ - typst color work
86
+ - test typst install
87
+ - typst in animation
88
+
89
+
90
+
91
+
@@ -0,0 +1,9 @@
1
+ manim_derivative_plotter-1.0.0.dist-info/licenses/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
2
+ manim_derivatives_plotter/__init__.py,sha256=3eNEw8gLM2sAaPM789JNGQ0i6ubntUEu0VPAZZ_itI0,644
3
+ manim_derivatives_plotter/derivatives_simple.py,sha256=IdaeWT823jklU5ai-QDQN3btj05rKOFKBsXxLnuhJ1U,17676
4
+ manim_derivatives_plotter/useful_derivatives.py,sha256=OY9jN0LryoWNUXQ_x9eAbWwHfUl_2DMWIeZJZcLUO9E,19009
5
+ manim_derivative_plotter-1.0.0.dist-info/METADATA,sha256=iWDCstzhj4j9aosDxwIOH_G3xCOiqW_aj4SNMZDOr48,3548
6
+ manim_derivative_plotter-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ manim_derivative_plotter-1.0.0.dist-info/entry_points.txt,sha256=qoK4TKKTQxeK0CwPjxd0H3pcCQ7-tOD2eyf6AZnuiJg,140
8
+ manim_derivative_plotter-1.0.0.dist-info/top_level.txt,sha256=GCPKp7tPgqickw-nXqpU8K6tZw8Vh6ajeEcoFPYFocM,26
9
+ manim_derivative_plotter-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ render-animation = manim_derivatives.__init__:render_animation
3
+ render-picture = manim_derivatives.__init__:render_picture
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ manim_derivatives_plotter
@@ -0,0 +1,32 @@
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+
6
+ PACKAGE_DIR = Path(__file__).parent
7
+
8
+ def render_animation():
9
+ subprocess.run(
10
+ [
11
+ sys.executable,
12
+ "-m",
13
+ "manim",
14
+ "-pqh",
15
+ str(PACKAGE_DIR / "useful_derivatives.py"),
16
+ "Derivatives",
17
+ ],
18
+ check=True,
19
+ )
20
+
21
+ def render_picture():
22
+ subprocess.run(
23
+ [
24
+ sys.executable,
25
+ "-m",
26
+ "manim",
27
+ "-pqh",
28
+ str(PACKAGE_DIR / "derivatives_simple.py"),
29
+ "Derivatives",
30
+ ],
31
+ check=True,
32
+ )
@@ -0,0 +1,498 @@
1
+ from manim import *
2
+ import sympy as sp
3
+ import numpy as np
4
+ final_coefficients=[]
5
+ final_powers=[]
6
+ null_points=[]
7
+ extreme_points=[]
8
+ turning_points=[]
9
+
10
+ zeros_x_coords=[[],[],[]]
11
+ class Derivatives(Scene):
12
+
13
+ def construct(self):
14
+
15
+ coefficients=[]
16
+ powers=[]
17
+ accepted=False
18
+
19
+ #get User Input
20
+
21
+ while accepted==False:
22
+ accepted=True
23
+ func=str(input())
24
+ #fu=func.split("+")
25
+ fu=[""]
26
+ #seperate equations
27
+ element=0
28
+ for i,f in enumerate(func):
29
+ if f != "+" and f != "-":
30
+
31
+ fu[element]+=f
32
+ else:
33
+ if f=="+":
34
+ element+=1
35
+ fu.append("")
36
+ elif f=="-":
37
+
38
+ if i !=0:
39
+ element+=1
40
+ fu.append("")
41
+ fu[element]+="-"
42
+
43
+
44
+ for f in fu: #make equations to numbers
45
+ has_x=False
46
+ coefficient=""
47
+ power=""
48
+ for a in f:
49
+
50
+ if 47<ord(a)<58 or a=="-" or a==".":
51
+ if has_x != True:
52
+ coefficient=coefficient+a
53
+ else:
54
+ power=power+a
55
+ else:
56
+ has_x=True
57
+ try:
58
+ if coefficient=="":
59
+ coefficient="1"
60
+ if coefficient=="-":
61
+ coefficient="-1"
62
+ if has_x==False:
63
+ power=0
64
+ if power=="":
65
+ power=1
66
+ coefficients.append(float(coefficient))
67
+ powers.append(int(power))
68
+ except:
69
+ accepted=False
70
+ print("Invalid Input")
71
+
72
+
73
+
74
+
75
+ self.create_functions(coefficients,powers)
76
+
77
+ # find value range of axes based on zeros and y coordinates, so you can see all the functions and all the interesting points
78
+ highest_x_value=0
79
+ lowest_x_value=0
80
+ for ze in zeros_x_coords:
81
+ for z in ze:
82
+ if z>highest_x_value:
83
+ highest_x_value=z
84
+ if z<lowest_x_value:
85
+ lowest_x_value=z
86
+ highest_y_value=0
87
+ lowest_y_value=0
88
+ for ex in extreme_points:
89
+ if ex[1]>highest_y_value:
90
+ highest_y_value=ex[1]
91
+ if ex[1]<lowest_y_value:
92
+ lowest_y_value=ex[1]
93
+ for tu in turning_points:
94
+ if tu[1]>highest_y_value:
95
+ highest_y_value=tu[1]
96
+ if tu[1]<lowest_y_value:
97
+ lowest_y_value=tu[1]
98
+
99
+ for i,(co,po) in enumerate(zip(final_coefficients,final_powers)):
100
+ if i !=3:
101
+ y=self.final_function(0,co,po)
102
+ if y<lowest_y_value:
103
+ lowest_y_value=y
104
+ if y>highest_y_value:
105
+ highest_y_value=y
106
+
107
+ #create axes
108
+
109
+ x_val=1.5*max(highest_x_value,abs(lowest_x_value))
110
+ y_val=2*max(highest_y_value,abs(lowest_y_value))
111
+
112
+
113
+ if 0>x_val>-1:
114
+ x_val=-1
115
+ elif 1>x_val>0:
116
+ x_val=1
117
+ if 0>y_val>-1:
118
+ y_val=-1
119
+ elif 1>y_val>0:
120
+ y_val=1
121
+
122
+ x_val=round(x_val)
123
+ y_val=round(y_val)
124
+ if x_val==0:
125
+ x_val=5
126
+ if y_val==0:
127
+ y_val=5
128
+ x_step=round(x_val/4)
129
+ if x_step==0:
130
+ x_step=1
131
+ y_step=round(y_val/4)
132
+ if y_step==0:
133
+ y_step=1
134
+
135
+ ax=Axes(
136
+ (-x_val,x_val,x_step),
137
+ (-y_val,y_val,y_step),
138
+ 13,
139
+ 7,
140
+
141
+ x_axis_config={
142
+ "include_numbers": True,
143
+ "include_tip": False,
144
+
145
+
146
+ },
147
+ y_axis_config={
148
+ "include_numbers": True,
149
+ "include_tip": False,
150
+
151
+ },
152
+ ).set_z_index(-3)
153
+
154
+
155
+ # define a bunch of starting variables
156
+ self.add(ax)
157
+ curves=[]
158
+ colors=color_gradient([BLUE,GREEN,RED],3)
159
+ texts=[]
160
+ zeros=VGroup()
161
+ ze=Typst("Zeros").scale(0.7).to_corner(DL).shift(2.5*UP)
162
+ zeros.add(ze)
163
+ self.add(ze)
164
+ hiding_rects=VGroup(
165
+ Rectangle(stroke_width=0,fill_color=BLACK,fill_opacity=0.5) for i in range(0,4)
166
+ )
167
+ hiding_rects[0].to_corner(UL)
168
+ hiding_rects[1].to_corner(DL)
169
+ hiding_rects[2].to_corner(UR)
170
+ hiding_rects[3].to_corner(DR)
171
+ longest_widths=[1,1,1,1]
172
+ tects=VGroup()
173
+ final_ranges=[]
174
+
175
+ for i in range(0,3): # Draw Functions and Zeros (goes through all three functions)
176
+ #plot functions and zeros
177
+ coefficients=final_coefficients[i]
178
+ powers=final_powers[i]
179
+ if i==0:
180
+ op=1
181
+ else:
182
+ op=0.5
183
+
184
+ ranges=[]
185
+ in_frame=False
186
+ current_lower_boundary=round(-x_val)
187
+ current_upper_boundary=round(x_val)
188
+ cur=[]
189
+ #find ranges where the functions is in the visible range
190
+ for x in np.arange(float(-x_val),float(x_val),0.1):
191
+
192
+ if abs(self.final_function(x,coefficients,powers))<abs(y_val):
193
+ if not in_frame:
194
+ current_lower_boundary=x-0.1
195
+ in_frame=True
196
+ else:
197
+ if in_frame:
198
+ current_upper_boundary=x+0.1
199
+ ranges.append([current_lower_boundary,current_upper_boundary])
200
+ in_frame=False
201
+ if ranges==[]:
202
+ ranges.append([current_lower_boundary,current_upper_boundary])
203
+ #draw function graph
204
+ for r in ranges:
205
+ cur.append(ax.plot(lambda x:self.final_function(x,coefficients,powers),color=colors[i],x_range=(r[0],r[1])).set_stroke(opacity=op).set_z_index(-2))
206
+ final_ranges.append(ranges)
207
+ tec:MathTypst
208
+ curves.append(cur)
209
+ #write function
210
+ if i==0:
211
+ text=rf"f(x)="
212
+ elif i==1:
213
+ text=rf"f'(x)="
214
+ elif i==2:
215
+ text=rf"f''(x)="
216
+ for j,co in enumerate(zip(coefficients,powers)):
217
+ if j==0 or str(co[0])[0]=="-" or co[1]<0:
218
+ pass
219
+ else:
220
+ text+="+"
221
+ if co[1]==0:
222
+ text=text+rf"{co[0]}"
223
+ elif co[1]==1:
224
+ text=text+rf"{co[0]}*x"
225
+ elif co[1]<0:
226
+ text=text
227
+ else:
228
+ text=text+rf"{co[0]}*x^{co[1]}"
229
+ if i==0:
230
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL)
231
+ texts.append(tec.copy())
232
+ elif i==1:
233
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL).shift(0.5*DOWN)
234
+ texts.append(tec.copy())
235
+ elif i==2:
236
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL).shift(DOWN)
237
+
238
+ texts.append(tec.copy())
239
+
240
+
241
+
242
+
243
+ #create rectangles for better readability
244
+ tects.add(tec)
245
+ longest_widths[0]=max(longest_widths[0],tec.get_width())
246
+ hiding_rects[0].stretch_to_fit_height(i*0.5+0.5)
247
+ hiding_rects[0].stretch_to_fit_width(max(longest_widths[0],tec.get_width()))
248
+ hiding_rects[0].move_to(tects.get_center())
249
+ hiding_rects[0].align_to(tec,DOWN).set_z_index(-1)
250
+
251
+
252
+ solutions=zeros_x_coords[i]
253
+
254
+
255
+ current_zeros=[]
256
+
257
+ # create zero points and zero texts
258
+ points=VGroup()
259
+ if solutions:
260
+
261
+ for s in solutions:
262
+ so=s.evalf(3)
263
+ ze=Typst(str(so),color=colors[i]).scale(0.5).to_corner(DL).shift(RIGHT*(len(current_zeros)))
264
+ zeros.add(ze)
265
+ current_zeros.append(ze)
266
+
267
+ points=VGroup(
268
+ *[Dot(ax.c2p(sol, 0), color=colors[i]) for sol in solutions]
269
+ )
270
+
271
+
272
+
273
+
274
+ zero_v_group=VGroup(*current_zeros).shift(UP*(2.5-(0.5*i+0.5)))
275
+ longest_widths[1]=max(longest_widths[1],zero_v_group.get_width())
276
+ hiding_rects[1].stretch_to_fit_height(i*0.5+1)
277
+ hiding_rects[1].stretch_to_fit_width(max(zero_v_group.get_width(),longest_widths[1]))
278
+ hiding_rects[1].move_to(zeros.get_center())
279
+ hiding_rects[1].align_to(zero_v_group,DOWN).set_z_index(-1)
280
+
281
+ if not points:
282
+ points=VGroup()
283
+ null_points.append(points)
284
+
285
+ #add all of the above
286
+ self.add(
287
+ VGroup(*cur),
288
+ texts[i],
289
+ points,
290
+ zero_v_group
291
+ )
292
+ if len(zeros) !=0:
293
+ self.add(
294
+
295
+ hiding_rects[1]
296
+ )
297
+
298
+
299
+
300
+
301
+ #Extreme Points
302
+ title=Typst("Extreme Points").scale(0.7).to_corner(UR)
303
+
304
+
305
+
306
+
307
+ extremes=VGroup(
308
+ *[Dot(ax.c2p(ex[0], ex[1]), color=colors[1]) for ex in extreme_points]
309
+ )
310
+ extreme_labels=VGroup(
311
+ *[Text(rf"{ex[2]} ({ex[0]} ,{ex[1]})",color=colors[1]).scale(0.35).to_corner(UR).shift(DOWN*(i/2+0.5)) for i,ex in enumerate(extreme_points)]
312
+ )
313
+ hiding_rects[2].stretch_to_fit_height(len(extreme_labels)*0.5+0.5)
314
+ hiding_rects[2].stretch_to_fit_width(extreme_labels.get_width())
315
+ hiding_rects[2].move_to(extreme_labels.get_center())
316
+ hiding_rects[2].align_to(extreme_labels,DOWN).set_z_index(-1)
317
+
318
+
319
+
320
+ tu=Typst("Turning Points").scale(0.7).to_corner(DR).shift(2.5*UP)
321
+
322
+ #Turning Points
323
+
324
+
325
+ turns=VGroup(
326
+ *[Dot(ax.c2p(tu[0], tu[1]), color=colors[2]) for tu in turning_points]
327
+ )
328
+ turning_labels=VGroup(
329
+ *[Text(rf"{tu[2]} ({tu[0]} ,{tu[1]})",color=colors[2],t2c={"R":YELLOW,"L":PURPLE}).scale(0.35).to_corner(DR).shift(UP*(2-(i/2))) for i,tu in enumerate(turning_points)]
330
+ )
331
+ hiding_rects[3].stretch_to_fit_height(len(turning_labels)*0.5+0.5)
332
+ hiding_rects[3].stretch_to_fit_width(turning_labels.get_width())
333
+ hiding_rects[3].move_to(turning_labels.get_center())
334
+ hiding_rects[3].align_to(turning_labels,DOWN).set_z_index(-1)
335
+
336
+ #create graphs to show curvature
337
+ show_curves=[]
338
+ for i in range(0,len(turning_points)):
339
+
340
+ if turning_points[i][2][0]=="R":
341
+ col=YELLOW
342
+ else:
343
+ col=PURPLE
344
+ if i==0:
345
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(-x_val,turning_points[i][0])).set_z_index(-2)
346
+ else:
347
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(turning_points[i-1][0],turning_points[i][0])).set_z_index(-2)
348
+ show_curves.append(cur)
349
+
350
+ if turning_points !=[]:
351
+ if turning_points[len(turning_points)-1][2][2]=="R":
352
+ col=YELLOW
353
+ else:
354
+ col=PURPLE
355
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(turning_points[len(turning_points)-1][0],x_val)).set_z_index(-2)
356
+ show_curves.append(cur)
357
+ else:
358
+
359
+ second_derivative_value=self.final_function(0,final_coefficients[2],final_powers[2])
360
+ if second_derivative_value !=0:
361
+
362
+ if second_derivative_value<0:
363
+ col=YELLOW
364
+ else:
365
+ col=PURPLE
366
+
367
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(-x_val,x_val))#.set_z_index(-2)
368
+ show_curves.append(cur)
369
+ else:
370
+ pass
371
+
372
+ if len(null_points) >=2:
373
+ self.remove(
374
+ null_points[1],
375
+
376
+ )
377
+ if len(null_points)>=3:
378
+ self.remove(
379
+
380
+ null_points[2]
381
+ )
382
+ self.add(
383
+ title,
384
+ VGroup(*extreme_labels),
385
+ VGroup(*extremes),
386
+ tu,
387
+ VGroup(*turning_labels),
388
+ VGroup(*turns),
389
+ VGroup(*show_curves),
390
+ hiding_rects[0],
391
+ hiding_rects[2],
392
+ hiding_rects[3],
393
+
394
+
395
+ )
396
+ self.wait(3)
397
+
398
+
399
+ def create_functions(self,coefficients,powers):
400
+ #calculate all important values
401
+ for i in range(0,4):
402
+ final_coefficients.append(coefficients.copy())
403
+ final_powers.append(powers.copy())
404
+ solutions=self.zero_points(coefficients,powers,i)
405
+ if i !=3:
406
+ if solutions:
407
+ try:
408
+ for s in solutions:
409
+
410
+
411
+
412
+ zeros_x_coords[i].append(s)
413
+
414
+ except:
415
+ print("weird zeros, don't worry")
416
+ zeros_x_coords[i].sort()
417
+ for i in range(0,len(powers)):
418
+ coefficients[i],powers[i]=self.make_derivative(coefficients[i],powers[i])
419
+
420
+
421
+ def make_derivative(self,co,pow):
422
+ co=round(co*pow,3)
423
+ if pow >0:
424
+ pow-=1
425
+ return co,pow
426
+ def final_function(self,x,co,po):
427
+ #calculate function value at point x
428
+ y=0
429
+ for i in range(0,len(po)):
430
+ if po[i]>=0:
431
+ y=y+co[i]*pow(x,po[i])
432
+ return y
433
+ def zero_points(self,co,po,iteration):
434
+ x = sp.Symbol('x')
435
+ a=0
436
+ for i in range(0,len(po)):
437
+ if po[i]>=0:
438
+ a=a+co[i]*x**po[i]
439
+
440
+ points=sp.solveset(a,x,domain=sp.S.Reals)
441
+
442
+ if co[0]==0 and po[0]<=0:
443
+ points=sp.solveset(1,x,domain=sp.S.Reals)
444
+
445
+ if iteration==1 and points:
446
+
447
+ for p in points:
448
+ extreme_points.append([p.evalf(3),self.final_function(p.evalf(3),final_coefficients[0],final_powers[0]).evalf(3)])
449
+ extreme_points.sort(key=lambda item: item[0])
450
+ if iteration==2:
451
+ for p in extreme_points: #add type to extreme points
452
+ value=""
453
+ second_derivative_value=self.final_function(p[0],final_coefficients[2],final_powers[2])
454
+
455
+ if second_derivative_value==0:
456
+ value="TEP"
457
+ if second_derivative_value>0:
458
+ value="TIP"
459
+ if second_derivative_value<0:
460
+ value="HOP"
461
+ p.append(value)
462
+ if points:
463
+
464
+ for p in points:
465
+
466
+ turning_points.append([p.evalf(3),self.final_function(p.evalf(3),final_coefficients[0],final_powers[0]).evalf(3)])
467
+
468
+ turning_points.sort(key=lambda item: item[0])
469
+
470
+
471
+ if iteration==3:
472
+ for p in turning_points: #add type to turning points
473
+ value=""
474
+ third_derivative_value=self.final_function(p[0],final_coefficients[3],final_powers[3])
475
+
476
+ if third_derivative_value==0:
477
+ left_value=self.final_function(p[0].evalf(3)-0.01,final_coefficients[2],final_powers[2])
478
+ right_value=self.final_function(p[0].evalf(3)+0.01,final_coefficients[2],final_powers[2])
479
+
480
+ value=""
481
+ if left_value>0:
482
+ value=value+"L-"
483
+ else:
484
+ value=value+"R-"
485
+
486
+ if right_value>0:
487
+ value=value+"L"
488
+ else:
489
+ value=value+"R"
490
+ if third_derivative_value>0:
491
+ value="R-L"
492
+ if third_derivative_value<0:
493
+ value="L-R"
494
+ p.append(value)
495
+
496
+
497
+ return(points)
498
+
@@ -0,0 +1,509 @@
1
+ from manim import *
2
+ import sympy as sp
3
+
4
+ #Global variables
5
+ final_coefficients=[]
6
+ final_powers=[]
7
+ null_points=[]
8
+ extreme_points=[]
9
+ turning_points=[]
10
+
11
+ zeros_x_coords=[[],[],[]]
12
+
13
+
14
+ class Derivatives(Scene):
15
+
16
+ def construct(self):
17
+ #get User Input
18
+ coefficients=[]
19
+ powers=[]
20
+ #func= "-4.5x10+4x2+-3x1" for programmer side input
21
+ #fu=func.split("+")
22
+ accepted=False
23
+ while accepted==False:
24
+ accepted=True
25
+ func=str(input())
26
+ #fu=func.split("+")
27
+ fu=[""]
28
+ #seperate equations
29
+ element=0
30
+ for i,f in enumerate(func):
31
+ if f != "+" and f != "-":
32
+
33
+ fu[element]+=f
34
+ else:
35
+ if f=="+":
36
+ element+=1
37
+ fu.append("")
38
+ elif f=="-":
39
+
40
+ if i !=0:
41
+ element+=1
42
+ fu.append("")
43
+ fu[element]+="-"
44
+
45
+
46
+ for f in fu: #make equations to numbers
47
+ has_x=False
48
+ coefficient=""
49
+ power=""
50
+ for a in f:
51
+
52
+ if 47<ord(a)<58 or a=="-" or a==".":
53
+ if has_x != True:
54
+ coefficient=coefficient+a
55
+ else:
56
+ power=power+a
57
+ else:
58
+ has_x=True
59
+ try:
60
+ if coefficient=="":
61
+ coefficient="1"
62
+ if coefficient=="-":
63
+ coefficient="-1"
64
+ if has_x==False:
65
+ power=0
66
+ if power=="":
67
+ power=1
68
+ coefficients.append(float(coefficient))
69
+ powers.append(int(power))
70
+ except:
71
+ accepted=False
72
+ print("Invalid Input")
73
+
74
+ #create axes
75
+
76
+ self.create_functions(coefficients,powers) #create functions
77
+
78
+ # find value range of axes based on zeros and y coordinates, so you can see all the functions and all the interesting points
79
+ highest_x_value=0
80
+ lowest_x_value=0
81
+ for ze in zeros_x_coords:
82
+ for z in ze:
83
+ if z>highest_x_value:
84
+ highest_x_value=z
85
+ if z<lowest_x_value:
86
+ lowest_x_value=z
87
+ highest_y_value=0
88
+ lowest_y_value=0
89
+ for ex in extreme_points:
90
+ if ex[1]>highest_y_value:
91
+ highest_y_value=ex[1]
92
+ if ex[1]<lowest_y_value:
93
+ lowest_y_value=ex[1]
94
+ for tu in turning_points:
95
+ if tu[1]>highest_y_value:
96
+ highest_y_value=tu[1]
97
+ if tu[1]<lowest_y_value:
98
+ lowest_y_value=tu[1]
99
+ for i,(co,po) in enumerate(zip(final_coefficients,final_powers)):
100
+ if i !=3:
101
+ y=self.final_function(0,co,po)
102
+ if y<lowest_y_value:
103
+ lowest_y_value=y
104
+ if y>highest_y_value:
105
+ highest_y_value=y
106
+ x_val=1.5*max(highest_x_value,abs(lowest_x_value))
107
+ y_val=2*max(highest_y_value,abs(lowest_y_value))
108
+ if 0>x_val>-1:
109
+ x_val=-1
110
+ elif 1>x_val>0:
111
+ x_val=1
112
+ if 0>y_val>-1:
113
+ y_val=-1
114
+ elif 1>y_val>0:
115
+ y_val=1
116
+ x_val=round(x_val)
117
+ y_val=round(y_val)
118
+ if x_val==0:
119
+ x_val=5
120
+ if y_val==0:
121
+ y_val=5
122
+ x_step=round(x_val/4)
123
+ if x_step==0:
124
+ x_step=1
125
+ y_step=round(y_val/4)
126
+ if y_step==0:
127
+ y_step=1
128
+ #create axes
129
+ ax=Axes(
130
+ (-x_val,x_val,x_step),
131
+ (-y_val,y_val,y_step),
132
+ 13,
133
+ 7,
134
+
135
+ x_axis_config={
136
+ "include_numbers": True,
137
+ "include_tip": False,
138
+
139
+
140
+ },
141
+ y_axis_config={
142
+ "include_numbers": True,
143
+ "include_tip": False,
144
+
145
+ },
146
+ ).set_z_index(-3)
147
+
148
+
149
+ # define a bunch of starting variables
150
+ self.add(ax)
151
+ curves=[]
152
+ colors=color_gradient([BLUE,GREEN,RED],3)
153
+ tex=MathTypst("f(x)=").to_corner(UL).scale(0.5)
154
+ zeros=VGroup()
155
+ ze=Typst("Zeros").scale(0.7).to_corner(DL).shift(2.5*UP)
156
+ zeros.add(ze)
157
+ self.add(ze)
158
+ texts=[]
159
+ texts.append(tex)
160
+ hiding_rects=VGroup(
161
+ Rectangle(stroke_width=0,fill_color=BLACK,fill_opacity=0.5) for i in range(0,4)
162
+ )
163
+ hiding_rects[0].to_corner(UL)
164
+ hiding_rects[1].to_corner(DL)
165
+ hiding_rects[2].to_corner(UR)
166
+ hiding_rects[3].to_corner(DR)
167
+ longest_widths=[1,1,1,1]
168
+ self.add(tex)
169
+ tects=VGroup()
170
+ final_ranges=[]
171
+ for i in range(0,3): # Draw Functions and Zeros (goes through all three functions)
172
+
173
+
174
+ coefficients=final_coefficients[i]
175
+ powers=final_powers[i]
176
+ if i==0:
177
+ op=1
178
+ else:
179
+ op=0.5
180
+ ranges=[]
181
+ in_frame=False
182
+ current_lower_boundary=round(-x_val)
183
+ current_upper_boundary=round(x_val)
184
+ cur=[]
185
+ #find ranges where the functions is in the visible range
186
+ for x in np.arange(float(-x_val),float(x_val),0.1):
187
+
188
+ if abs(self.final_function(x,coefficients,powers))<abs(y_val):
189
+ if not in_frame:
190
+ current_lower_boundary=x-0.1
191
+ in_frame=True
192
+ else:
193
+ if in_frame:
194
+ current_upper_boundary=x+0.1
195
+ ranges.append([current_lower_boundary,current_upper_boundary])
196
+ in_frame=False
197
+ if ranges==[]:
198
+ ranges.append([current_lower_boundary,current_upper_boundary])
199
+ #draw function graph
200
+ for r in ranges:
201
+ cur.append(ax.plot(lambda x:self.final_function(x,coefficients,powers),color=colors[i],x_range=(r[0],r[1])).set_stroke(opacity=op).set_z_index(-2))
202
+ curves.append(cur)
203
+ final_ranges.append(ranges)
204
+ #write function
205
+ if i==0:
206
+ text=rf"f(x)="
207
+ elif i==1:
208
+ text=rf"f'(x)="
209
+ elif i==2:
210
+ text=rf"f''(x)="
211
+ for j,co in enumerate(zip(coefficients,powers)):
212
+ if j==0 or str(co[0])[0]=="-" or co[1]<0:
213
+ pass
214
+ else:
215
+ text+="+"
216
+ if co[1]==0:
217
+ text=text+rf"{co[0]}"
218
+ elif co[1]==1:
219
+ text=text+rf"{co[0]}*x"
220
+ elif co[1]<0:
221
+ text=text
222
+ else:
223
+ text=text+rf"{co[0]}*x^{co[1]}"
224
+ if i==0:
225
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL)
226
+ texts.append(tec.copy())
227
+ elif i==1:
228
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL).shift(0.5*DOWN)
229
+ texts.append(tec.copy())
230
+ elif i==2:
231
+ tec=MathTypst(text,color=colors[i]).scale(0.5).to_corner(UL).shift(DOWN)
232
+
233
+ texts.append(tec.copy())
234
+ #create rectangles for better readability
235
+ tects.add(tec)
236
+ longest_widths[0]=max(longest_widths[0],tec.get_width())
237
+ hiding_rects[0].stretch_to_fit_height(i*0.5+0.5)
238
+ hiding_rects[0].stretch_to_fit_width(max(longest_widths[0],tec.get_width()))
239
+ hiding_rects[0].move_to(tects.get_center())
240
+ hiding_rects[0].align_to(tec,DOWN).set_z_index(-1)
241
+
242
+
243
+ solutions=zeros_x_coords[i]
244
+ # create zero points and zero texts
245
+ points=VGroup()
246
+ current_zeros=[]
247
+ if solutions:
248
+ for s in solutions:
249
+ so=s.evalf(3)
250
+ ze=Typst(str(so),color=colors[i]).scale(0.5).to_corner(DL).shift(RIGHT*(len(current_zeros)))
251
+ zeros.add(ze)
252
+ current_zeros.append(ze)
253
+
254
+ points=VGroup(
255
+ *[Dot(ax.c2p(sol, 0), color=colors[i]) for sol in solutions]
256
+ )
257
+
258
+ zero_v_group=VGroup(*current_zeros).shift(UP*(2.5-(0.5*i+0.5)))
259
+ longest_widths[1]=max(longest_widths[1],zero_v_group.get_width())
260
+ hiding_rects[1].stretch_to_fit_height(i*0.5+1)
261
+ hiding_rects[1].stretch_to_fit_width(max(zero_v_group.get_width(),longest_widths[1]))
262
+ hiding_rects[1].move_to(zeros.get_center())
263
+ hiding_rects[1].align_to(zero_v_group,DOWN).set_z_index(-1)
264
+ if not points:
265
+ points=VGroup()
266
+ null_points.append(points)
267
+
268
+ self.remove(
269
+ hiding_rects[0],
270
+ hiding_rects[1]
271
+ )
272
+ self.add(
273
+ hiding_rects[0],
274
+
275
+ )
276
+ if len(points) !=0:
277
+ self.add(
278
+
279
+ hiding_rects[1]
280
+ )
281
+ #animate all of the above
282
+ self.play(
283
+ Create(VGroup(*cur)),
284
+ Transform(
285
+ texts[i],
286
+ tec
287
+ ),
288
+ run_time=3
289
+ )
290
+
291
+ self.play(
292
+ Create(points),
293
+ Write(zero_v_group),
294
+ run_time=2
295
+ )
296
+
297
+
298
+ if len(null_points)<4:
299
+ for i in range(0,2):
300
+ null_points.append(Dot(1000,0))
301
+
302
+ #Extreme Points
303
+
304
+ title=Typst("Extreme Points").scale(0.7).to_corner(UR)
305
+
306
+ self.wait(2)
307
+ self.play(
308
+ FadeOut(VGroup(*curves[2])),
309
+ FadeOut(texts[2]),
310
+ FadeOut(null_points[2]),
311
+ FadeOut(null_points[0]),
312
+ Write(title),
313
+ run_time=3
314
+ )
315
+
316
+ extremes=VGroup(
317
+ *[Dot(ax.c2p(ex[0], ex[1]), color=colors[0]) for ex in extreme_points]
318
+ )
319
+ extreme_labels=VGroup(
320
+ *[Text(rf"{ex[2]} ({ex[0]} ,{ex[1]})",color=colors[0]).scale(0.35).to_corner(UR).shift(DOWN*(i/2+0.5)) for i,ex in enumerate(extreme_points)]
321
+ )
322
+ hiding_rects[2].stretch_to_fit_height(len(extreme_labels)*0.5+0.5)
323
+ hiding_rects[2].stretch_to_fit_width(extreme_labels.get_width())
324
+ hiding_rects[2].move_to(extreme_labels.get_center())
325
+ hiding_rects[2].align_to(extreme_labels,DOWN).set_z_index(-1)
326
+
327
+ null_points_v_group=VGroup(*null_points[1])
328
+ self.play(
329
+ Write(VGroup(*extreme_labels)),
330
+ *[
331
+ ReplacementTransform(null_points_v_group[j],VGroup(*extremes)[j])
332
+ for j in range(0,len(extremes))
333
+ ],
334
+
335
+ Create(hiding_rects[2]),
336
+ run_time=3
337
+ )
338
+ self.wait(2)
339
+ tu=Typst("Turning Points").scale(0.7).to_corner(DR).shift(2.5*UP)
340
+ self.play(
341
+
342
+ FadeIn(VGroup(*curves[2])),
343
+ FadeIn(texts[2]),
344
+ FadeIn(null_points[2]),
345
+ FadeOut(extremes),
346
+ Write(tu),
347
+ run_time=3
348
+ )
349
+ #Turning Points
350
+
351
+
352
+ turns=VGroup(
353
+ *[Dot(ax.c2p(tu[0], tu[1]), color=colors[0]) for tu in turning_points]
354
+ )
355
+ turning_labels=Group(
356
+ *[Text(rf"{tu[2]} ({tu[0]} ,{tu[1]})",color=colors[0],t2c={"R":YELLOW,"L":PURPLE}).scale(0.35).to_corner(DR).shift(UP*(2-(i/2))) for i,tu in enumerate(turning_points)]
357
+ )
358
+ hiding_rects[3].stretch_to_fit_height(len(turning_labels)*0.5+0.5)
359
+ hiding_rects[3].stretch_to_fit_width(turning_labels.get_width())
360
+ hiding_rects[3].move_to(turning_labels.get_center())
361
+ hiding_rects[3].align_to(turning_labels,DOWN).set_z_index(-1)
362
+
363
+ show_curves=[]
364
+ extremes_first_derivative=[]
365
+ #create graphs to show curvature
366
+ for i in range(0,len(turning_points)):
367
+ extremes_first_derivative.append(Dot(ax.c2p(turning_points[i][0],self.final_function(turning_points[i][0].evalf(3),final_coefficients[1],final_powers[1])),color=colors[1]))
368
+ if turning_points[i][2][0]=="R":
369
+ col=YELLOW
370
+ else:
371
+ col=PURPLE
372
+ if i==0:
373
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(-x_val,turning_points[i][0])).set_z_index(-2)
374
+ else:
375
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(turning_points[i-1][0],turning_points[i][0])).set_z_index(-2)
376
+ show_curves.append(cur)
377
+ if turning_points!=[]:
378
+ if turning_points[len(turning_points)-1][2][2]=="R":
379
+ col=YELLOW
380
+ else:
381
+ col=PURPLE
382
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(turning_points[len(turning_points)-1][0],x_val)).set_z_index(-2)
383
+ show_curves.append(cur)
384
+ self.play(
385
+ ReplacementTransform(VGroup(*null_points[2]),VGroup(*extremes_first_derivative)),
386
+ run_time=2
387
+ )
388
+ self.play(
389
+ Create(VGroup(*show_curves)),
390
+ Write(VGroup(*turning_labels)),
391
+ ReplacementTransform(VGroup(*extremes_first_derivative),VGroup(*turns)),
392
+ Create(hiding_rects[3]),
393
+ run_time=3
394
+ )
395
+ self.wait(3)
396
+ else:
397
+
398
+ second_derivative_value=self.final_function(0,final_coefficients[2],final_powers[2])
399
+ if second_derivative_value !=0:
400
+
401
+ if second_derivative_value<0:
402
+ col=YELLOW
403
+ else:
404
+ col=PURPLE
405
+
406
+ cur=ax.plot(lambda x:self.final_function(x,final_coefficients[0],final_powers[0]),color=col,x_range=(-x_val,x_val))#.set_z_index(-2)
407
+ show_curves.append(cur)
408
+ self.play(
409
+ Create(VGroup(*show_curves)),
410
+
411
+ run_time=3
412
+ )
413
+
414
+ else:
415
+ pass
416
+ self.wait(3)
417
+
418
+
419
+
420
+ def create_functions(self,coefficients,powers):
421
+ #calculate all important values
422
+ for i in range(0,4):
423
+ final_coefficients.append(coefficients.copy())
424
+ final_powers.append(powers.copy())
425
+ solutions=self.zero_points(coefficients,powers,i)
426
+ if i !=3:
427
+ if solutions:
428
+ try:
429
+ for s in solutions:
430
+
431
+
432
+
433
+ zeros_x_coords[i].append(s)
434
+
435
+ except:
436
+ print("weird zeros, don't worry")
437
+ zeros_x_coords[i].sort()
438
+ for i in range(0,len(powers)):
439
+ coefficients[i],powers[i]=self.make_derivative(coefficients[i],powers[i])
440
+
441
+ def make_derivative(self,co,pow):
442
+ co=round(co*pow,3)
443
+ if pow >0:
444
+ pow-=1
445
+ return co,pow
446
+ def final_function(self,x,co,po):
447
+ #calculate function value at point x
448
+ y=0
449
+ for i in range(0,len(po)):
450
+ if po[i]>=0:
451
+ y=y+co[i]*pow(x,po[i])
452
+ return y
453
+ def zero_points(self,co,po,iteration):
454
+ x = sp.Symbol('x')
455
+ a=0
456
+ for i in range(0,len(po)):
457
+ if po[i]>=0:
458
+ a=a+co[i]*x**po[i]
459
+
460
+ points=sp.solveset(a,x,domain=sp.S.Reals)
461
+
462
+ if co[0]==0 and po[0]<=0:
463
+ points=sp.solveset(1,x,domain=sp.S.Reals)
464
+
465
+ if iteration==1 and points:
466
+
467
+ for p in points:
468
+ extreme_points.append([p.evalf(3),self.final_function(p.evalf(3),final_coefficients[0],final_powers[0]).evalf(3)])
469
+ extreme_points.sort(key=lambda item: item[0])
470
+ if iteration==2:
471
+ for p in extreme_points: #add type to extreme points
472
+ value=""
473
+ second_derivative_value=self.final_function(p[0].evalf(3),final_coefficients[2],final_powers[2])
474
+ if second_derivative_value==0:
475
+ value="TEP"
476
+ if second_derivative_value>0:
477
+ value="TIP"
478
+ if second_derivative_value<0:
479
+ value="HOP"
480
+ p.append(value)
481
+ if points:
482
+ for p in points:
483
+ turning_points.append([p.evalf(3),self.final_function(p.evalf(3),final_coefficients[0],final_powers[0]).evalf(3)])
484
+ turning_points.sort(key=lambda item: item[0])
485
+ if iteration==3:
486
+ for p in turning_points: #add type to turning points
487
+ value=""
488
+ third_derivative_value=self.final_function(p[0].evalf(3),final_coefficients[3],final_powers[3])
489
+ if third_derivative_value==0:
490
+ left_value=self.final_function(p[0].evalf(3)-0.1,final_coefficients[2],final_powers[2])
491
+ right_value=self.final_function(p[0].evalf(3)+0.1,final_coefficients[2],final_powers[2])
492
+ value=""
493
+ if left_value>0:
494
+ value=value+"L-"
495
+ else:
496
+ value=value+"R-"
497
+ if right_value>0:
498
+ value=value+"L"
499
+ else:
500
+ value=value+"R"
501
+ if third_derivative_value>0:
502
+ value="R-L"
503
+ if third_derivative_value<0:
504
+ value="L-R"
505
+ p.append(value)
506
+
507
+
508
+ return(points)
509
+