├── .gitignore ├── BV1JX4y1K7Dr ├── Manim Animations │ ├── axis.py │ ├── graph_scene.py │ └── series.py └── markdown │ ├── Canon_formula.md │ └── EqualTemperament_def.md └── BV1YqZJYaEo5 └── milliniumMenu ├── back.mp4 ├── back_2.mp4 ├── back_covered.png ├── back_open.png ├── background.js ├── front.png ├── index.html ├── logo.png ├── script.js ├── styles.css └── wood.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | BV1JX4y1K7Dr/.DS_Store 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /BV1JX4y1K7Dr/Manim Animations/axis.py: -------------------------------------------------------------------------------- 1 | # use manim -c 'GREEN' -pl axis.py to change background color 2 | # new version of graph_scene.py file required for manim 3 | from manimlib.imports import * 4 | 5 | mode = 0 # 1 for black background, 0 for white 6 | black_white = [BLACK, WHITE] 7 | 8 | 9 | class CandleAxes(GraphScene): 10 | CONFIG = { 11 | "x_min": -3, 12 | "x_min": -1, 13 | "x_max": 11, 14 | "y_min": 0, 15 | "y_max": 1.2, 16 | "graph_origin": 3 * DOWN + 4 * LEFT, 17 | 18 | "x_tick_frequency": 10, 19 | "x_leftmost_tick": 0, 20 | # "x_axis_width": 8, 21 | 22 | "x_labeled_nums": [0, 2, 4, 6, 8, 10], 23 | # "x_labeled_nums": [-2, 0, 2, 3, 7, 10], 24 | "x_axis_label": "$x$ ", 25 | # "x_axis_label": "$t$ (min)", 26 | 27 | 28 | "y_labeled_nums": [1], 29 | "y_axis_label": "$g$", 30 | # "y_axis_label": "$m$ (la)", 31 | "exclude_zero_label": False, 32 | 33 | # to switch background 34 | "axes_color": black_white[mode], 35 | "label_color": black_white[mode], 36 | "label_nums_color": black_white[mode], 37 | "camera_config": {"background_color": black_white[1-mode]}, 38 | } 39 | 40 | def candle(self, t): 41 | if t <= 0: 42 | return 1 43 | elif t <= 10: 44 | return 1 - (0.1 * t) 45 | else: 46 | return 0 47 | 48 | def construct(self): 49 | self.setup_axes(animate=True) 50 | candle_curve = self.get_graph(self.candle, BLUE, x_min=-2, x_max=12) 51 | self.play(ShowCreation(candle_curve)) 52 | self.wait(1) 53 | 54 | 55 | class LightenAxis(GraphScene): 56 | CONFIG = { 57 | "x_min": -1, 58 | "x_max": 11, 59 | "y_min": 0, 60 | "y_max": 5.5, 61 | "graph_origin": 3 * DOWN + 4 * LEFT, 62 | 63 | "x_tick_frequency": 10, 64 | "y_tick_frequency": 100, 65 | "x_leftmost_tick": 0, 66 | 67 | "x_labeled_nums": [0, 4, 5, 9, 10], 68 | "x_axis_label": "$x$ ", 69 | # "x_axis_label": "$t$ (min)", 70 | 71 | "y_labeled_nums": [1, 2, 3, 4, 5], 72 | "y_axis_label": "$f$", 73 | # "y_axis_label": "$\#$candle", 74 | "exclude_zero_label": False, 75 | 76 | # to switch background 77 | "axes_color": black_white[mode], 78 | "label_color": black_white[mode], 79 | "label_nums_color": black_white[mode], 80 | "camera_config": {"background_color": black_white[1-mode]}, 81 | } 82 | 83 | def _draw(self, _shape, local_x, local_y): 84 | _shape.move_arc_center_to([self.coords_to_point(local_x, local_y)[ 85 | 0], self.coords_to_point(local_x, local_y)[1], 0]) 86 | return ShowCreation(_shape) 87 | 88 | def drawCircle(self, local_x, local_y): 89 | _circle = Circle( 90 | color=GREEN, 91 | # fill_color=WHITE, 92 | fill_color=black_white[1-mode], 93 | fill_opacity=1.0, 94 | radius=0.15, 95 | ) 96 | return self._draw(_circle, local_x, local_y) 97 | 98 | def drawDot(self, local_x, local_y): 99 | _dot = g2 = Dot( 100 | color=GREEN, 101 | radius=0.15, 102 | ) 103 | return self._draw(_dot, local_x, local_y) 104 | 105 | def zero(self, t): 106 | return 0 107 | 108 | def construct(self): 109 | self.setup_axes(animate=True) 110 | lighten_nb = self.get_graph(self.zero, GREEN, x_min=-2, x_max=12) 111 | self.play(ShowCreation(lighten_nb), 112 | self.drawCircle(0, 0), self.drawDot(0, 1), 113 | self.drawCircle(4, 0), self.drawDot(4, 4), 114 | # self.drawCircle(5, 0), self.drawDot(5, 3), 115 | self.drawCircle(5, 0), self.drawDot(5, 5), 116 | self.drawCircle(9, 0), self.drawDot(9, 2), 117 | ) 118 | 119 | self.wait(1) 120 | 121 | # curcurCircle1 = Circle( 122 | # color=BLUE, 123 | # radius=0.2, 124 | # ) 125 | # curcurCircle1_4 = Circle( 126 | # color=TEAL, 127 | # radius=0.2, 128 | # ) 129 | # curcurCircle4 = Circle( 130 | # color=TEAL, 131 | # radius=0.2, 132 | # ) 133 | # self.play(ShowCreation( 134 | # curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 135 | # self.play(ReplacementTransform( 136 | # curcurCircle1, 137 | # curcurCircle1_4.move_arc_center_to(self.coords_to_point(4, 0)))) 138 | # self.play(ReplacementTransform( 139 | # curcurCircle1_4, 140 | # curcurCircle4.move_arc_center_to(self.coords_to_point(4, 4)))) 141 | 142 | # curcurCircle1 = Circle( 143 | # color=BLUE, 144 | # radius=0.2, 145 | # ) 146 | # curcurCircle1_3 = Circle( 147 | # color=ORANGE, 148 | # radius=0.2, 149 | # ) 150 | # curcurCircle3 = Circle( 151 | # color=ORANGE, 152 | # radius=0.2, 153 | # ) 154 | # self.play(ShowCreation( 155 | # curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 156 | # self.play(ReplacementTransform( 157 | # curcurCircle1, 158 | # curcurCircle1_3.move_arc_center_to(self.coords_to_point(5, 0)))) 159 | # self.play(ReplacementTransform( 160 | # curcurCircle1_3, 161 | # curcurCircle3.move_arc_center_to(self.coords_to_point(5, 3)))) 162 | 163 | # curcurCircle1 = Circle( 164 | # color=BLUE, 165 | # radius=0.2, 166 | # ) 167 | # curcurCircle1_2 = Circle( 168 | # color=PURPLE, 169 | # radius=0.2, 170 | # ) 171 | # curcurCircle2 = Circle( 172 | # color=PURPLE, 173 | # radius=0.2, 174 | # ) 175 | # self.play(ShowCreation( 176 | # curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 177 | # self.play(ReplacementTransform( 178 | # curcurCircle1, 179 | # curcurCircle1_2.move_arc_center_to(self.coords_to_point(9, 0)))) 180 | # self.play(ReplacementTransform( 181 | # curcurCircle1_2, 182 | # curcurCircle2.move_arc_center_to(self.coords_to_point(9, 2)))) 183 | 184 | # curcurCircle1 = Circle( 185 | # color=BLUE, 186 | # radius=0.2, 187 | # ) 188 | # self.play(ShowCreation( 189 | # curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 190 | 191 | # self.wait(1) 192 | 193 | # self.play(FadeOut(curcurCircle1), FadeOut(curcurCircle2), 194 | # FadeOut(curcurCircle3), FadeOut(curcurCircle4)) 195 | # self.wait(1) 196 | 197 | 198 | class MultipleCandleSerial(GraphScene): 199 | CONFIG = { 200 | "x_min": -1, 201 | "x_max": 11, 202 | "y_min": 0, 203 | "y_max": 12, 204 | "graph_origin": 3 * DOWN + 4 * LEFT, 205 | 206 | "x_tick_frequency": 10, 207 | "y_tick_frequency": 10, 208 | "x_leftmost_tick": 0, 209 | 210 | "x_labeled_nums": [0, 2, 4, 6, 8, 10], 211 | "x_axis_label": "$t$ (min)", 212 | 213 | "y_labeled_nums": [1, 2, 10], 214 | "y_axis_label": "$m$ (la)", 215 | "exclude_zero_label": False, 216 | 217 | # to switch background 218 | "axes_color": black_white[mode], 219 | "label_color": black_white[mode], 220 | "label_nums_color": black_white[mode], 221 | "camera_config": {"background_color": black_white[1-mode]}, 222 | } 223 | 224 | def candle1(self, t): 225 | return CandleAxes.candle(self, t) 226 | 227 | def candle2(self, t): 228 | return 2 * CandleAxes.candle(self, t) 229 | 230 | def candle10(self, t): 231 | return 10 * CandleAxes.candle(self, t) 232 | 233 | def construct(self): 234 | self.setup_axes(animate=True) 235 | self.play(ShowPassingFlash(self.get_graph( 236 | self.candle1, BLUE, x_min=-2, x_max=12)), 237 | ) 238 | 239 | self.play(ShowPassingFlash(self.get_graph( 240 | self.candle2, PURPLE, x_min=-2, x_max=12)), 241 | ) 242 | 243 | self.play(ShowPassingFlash(self.get_graph( 244 | self.candle10, RED, x_min=-2, x_max=12)), 245 | ) 246 | 247 | self.play(AnimationGroup( 248 | ShowCreation(self.get_graph( 249 | self.candle1, BLUE, x_min=-2, x_max=11.5)), 250 | ShowCreation(self.get_graph( 251 | self.candle2, PURPLE, x_min=-2, x_max=12)), 252 | ShowCreation(self.get_graph( 253 | self.candle10, RED, x_min=-2, x_max=12.5)), 254 | 255 | lag_ratio=0.4)) 256 | 257 | self.wait(1) 258 | 259 | 260 | class MultipleCandleParallel(GraphScene): 261 | CONFIG = { 262 | "x_min": -1, 263 | "x_max": 20, 264 | "y_min": 0, 265 | "y_max": 12, 266 | "graph_origin": 3 * DOWN + 4 * LEFT, 267 | 268 | "x_tick_frequency": 19, 269 | "y_tick_frequency": 10, 270 | "x_leftmost_tick": 0, 271 | 272 | "x_labeled_nums": [0, 4, 5, 9, 10, 14, 15, 19], 273 | "x_axis_label": "$t$ (min)", 274 | 275 | "y_labeled_nums": [1, 2, 3, 4, 10], 276 | "y_axis_label": "$m$ (la)", 277 | "exclude_zero_label": False, 278 | 279 | # to switch background 280 | "axes_color": black_white[mode], 281 | "label_color": black_white[mode], 282 | "label_nums_color": black_white[mode], 283 | "camera_config": {"background_color": black_white[1-mode]}, 284 | } 285 | 286 | def candle1(self, t): 287 | return 1 * CandleAxes.candle(self, t) 288 | 289 | def candle1_4(self, t): 290 | return CandleAxes.candle(self, t - 4) 291 | 292 | def candle4(self, t): 293 | return 4 * CandleAxes.candle(self, t - 4) 294 | 295 | def candle3(self, t): 296 | return 3 * CandleAxes.candle(self, t - 5) 297 | 298 | def candle1_3(self, t): 299 | return CandleAxes.candle(self, t - 5) 300 | 301 | def candle2(self, t): 302 | return 2 * CandleAxes.candle(self, t - 9) 303 | 304 | def candle1_2(self, t): 305 | return CandleAxes.candle(self, t - 9) 306 | 307 | def candle_sum(self, t): 308 | return self.candle1(t) + \ 309 | self.candle2(t) + \ 310 | self.candle3(t) + \ 311 | self.candle4(t) 312 | 313 | def construct(self): 314 | self.setup_axes(animate=True) 315 | 316 | self.wait(1) 317 | 318 | C1 = self.get_graph( 319 | self.candle1, BLUE, x_min=-2, x_max=21) 320 | 321 | C2 = self.get_graph( 322 | self.candle2, PURPLE, x_min=-2, x_max=21) 323 | 324 | C3 = self.get_graph( 325 | self.candle3, ORANGE, x_min=-2, x_max=21) 326 | 327 | C4 = self.get_graph( 328 | self.candle4, TEAL, x_min=-2, x_max=21) 329 | 330 | C1_2 = self.get_graph( 331 | self.candle1_2, PURPLE, x_min=-2, x_max=21) 332 | 333 | C1_3 = self.get_graph( 334 | self.candle1_3, ORANGE, x_min=-2, x_max=21) 335 | 336 | C1_4 = self.get_graph( 337 | self.candle1_4, TEAL, x_min=-2, x_max=21) 338 | 339 | C = self.get_graph( 340 | self.candle_sum, RED, x_min=-2, x_max=21) 341 | 342 | self.play(ShowCreation(C1)) 343 | self.play(ReplacementTransform(C1, C1_4)) 344 | self.play(ReplacementTransform(C1_4, C4)) 345 | # self.play(FadeOut(C4)) 346 | 347 | C1 = self.get_graph( 348 | self.candle1, BLUE, x_min=-2, x_max=21) 349 | 350 | self.play(ShowCreation(C1)) 351 | self.play(ReplacementTransform(C1, C1_3)) 352 | self.play(ReplacementTransform(C1_3, C3)) 353 | # self.play(FadeOut(C3)) 354 | 355 | C1 = self.get_graph( 356 | self.candle1, BLUE, x_min=-2, x_max=21) 357 | 358 | self.play(ShowCreation(C1)) 359 | self.play(ReplacementTransform(C1, C1_2)) 360 | self.play(ReplacementTransform(C1_2, C2)) 361 | # self.play(FadeOut(C2)) 362 | 363 | C1 = self.get_graph( 364 | self.candle1, BLUE, x_min=-2, x_max=21) 365 | 366 | self.play(ShowCreation(C1)) 367 | 368 | self.wait(1) 369 | 370 | self.play( 371 | ReplacementTransform(C1, C), 372 | ReplacementTransform(C2, C), 373 | ReplacementTransform(C3, C), 374 | ReplacementTransform(C4, C) 375 | ) 376 | 377 | self.wait(1) 378 | 379 | 380 | class LightenOnlyXAxis(GraphScene): 381 | CONFIG = { 382 | "x_min": -1, 383 | "x_max": 11, 384 | "y_min": 0.3, 385 | "y_max": 0.5, 386 | "graph_origin": 3 * DOWN + 4 * LEFT, 387 | 388 | "x_tick_frequency": 10, 389 | "x_leftmost_tick": 0, 390 | 391 | "x_labeled_nums": [0, 4, 5, 9, 10], 392 | "x_axis_label": "$t$ (min)", 393 | 394 | "y_axis_label": "", 395 | "exclude_zero_label": False, 396 | 397 | # to switch background 398 | "axes_color": black_white[mode], 399 | "label_color": black_white[mode], 400 | "label_nums_color": black_white[mode], 401 | "camera_config": {"background_color": black_white[1-mode]}, 402 | } 403 | 404 | def construct(self): 405 | self.setup_axes(animate=True) 406 | self.wait(1) 407 | 408 | 409 | class fAndGConvolution(GraphScene): 410 | CONFIG = { 411 | "x_min": -1, 412 | "x_max": 20, 413 | "y_min": 0, 414 | "y_max": 12, 415 | "graph_origin": 3 * DOWN + 4 * LEFT, 416 | 417 | "x_tick_frequency": 19, 418 | "y_tick_frequency": 10, 419 | "x_leftmost_tick": 0, 420 | 421 | "x_labeled_nums": [0, 4, 5, 9, 10, 14, 15, 19], 422 | "x_axis_label": "$x$", 423 | 424 | "y_labeled_nums": [1, 2, 3, 4, 10], 425 | "y_axis_label": "$y$", 426 | "exclude_zero_label": False, 427 | 428 | # to switch background 429 | "axes_color": black_white[mode], 430 | "label_color": black_white[mode], 431 | "label_nums_color": black_white[mode], 432 | "camera_config": {"background_color": black_white[1-mode]}, 433 | } 434 | 435 | def _draw(self, _shape, local_x, local_y): 436 | _shape.move_arc_center_to([self.coords_to_point(local_x, local_y)[ 437 | 0], self.coords_to_point(local_x, local_y)[1], 0]) 438 | return ShowCreation(_shape) 439 | 440 | def drawCircle(self, local_x, local_y): 441 | _circle = Circle( 442 | color=GREEN, 443 | # fill_color=WHITE, 444 | fill_color=black_white[1-mode], 445 | fill_opacity=1.0, 446 | radius=0.15, 447 | ) 448 | return self._draw(_circle, local_x, local_y) 449 | 450 | def drawDot(self, local_x, local_y): 451 | _dot = g2 = Dot( 452 | color=GREEN, 453 | radius=0.15, 454 | ) 455 | return self._draw(_dot, local_x, local_y) 456 | 457 | def zero(self, t): 458 | return 0 459 | 460 | def candle1(self, t): 461 | return 1 * CandleAxes.candle(self, t) 462 | 463 | def candle1_4(self, t): 464 | return CandleAxes.candle(self, t - 4) 465 | 466 | def candle4(self, t): 467 | return 4 * CandleAxes.candle(self, t - 4) 468 | 469 | def candle3(self, t): 470 | return 3 * CandleAxes.candle(self, t - 5) 471 | 472 | def candle1_3(self, t): 473 | return CandleAxes.candle(self, t - 5) 474 | 475 | def candle2(self, t): 476 | return 2 * CandleAxes.candle(self, t - 9) 477 | 478 | def candle1_2(self, t): 479 | return CandleAxes.candle(self, t - 9) 480 | 481 | def candle_sum(self, t): 482 | return self.candle1(t) + \ 483 | self.candle2(t) + \ 484 | self.candle3(t) + \ 485 | self.candle4(t) 486 | 487 | def construct(self): 488 | self.setup_axes(animate=True) 489 | 490 | self.wait(1) 491 | 492 | lighten_nb = self.get_graph(self.zero, GREEN, x_min=-2, x_max=21) 493 | 494 | C1 = self.get_graph( 495 | self.candle1, BLUE, x_min=-2, x_max=21) 496 | 497 | C1b = self.get_graph( 498 | self.candle1, BLUE, x_min=-2, x_max=21) 499 | 500 | C1c = self.get_graph( 501 | self.candle1, BLUE, x_min=-2, x_max=21) 502 | 503 | C1d = self.get_graph( 504 | self.candle1, BLUE, x_min=-2, x_max=21) 505 | 506 | C2 = self.get_graph( 507 | self.candle2, PURPLE, x_min=-2, x_max=21) 508 | 509 | C3 = self.get_graph( 510 | self.candle3, ORANGE, x_min=-2, x_max=21) 511 | 512 | C4 = self.get_graph( 513 | self.candle4, TEAL, x_min=-2, x_max=21) 514 | 515 | C1_2 = self.get_graph( 516 | self.candle1_2, PURPLE, x_min=-2, x_max=21) 517 | 518 | C1_3 = self.get_graph( 519 | self.candle1_3, ORANGE, x_min=-2, x_max=21) 520 | 521 | C1_4 = self.get_graph( 522 | self.candle1_4, TEAL, x_min=-2, x_max=21) 523 | 524 | C = self.get_graph( 525 | self.candle_sum, RED, x_min=-2, x_max=21) 526 | 527 | self.play(ShowCreation(lighten_nb), 528 | self.drawCircle(0, 0), self.drawDot(0, 1), 529 | self.drawCircle(4, 0), self.drawDot(4, 4), 530 | self.drawCircle(5, 0), self.drawDot(5, 3), 531 | self.drawCircle(9, 0), self.drawDot(9, 2), 532 | ShowCreation(C1) 533 | ) 534 | 535 | self.wait(1) 536 | 537 | curcurCircle1 = Circle( 538 | color=BLUE, 539 | radius=0.2, 540 | ) 541 | curcurCircle1_4 = Circle( 542 | color=TEAL, 543 | radius=0.2, 544 | ) 545 | curcurCircle4 = Circle( 546 | color=TEAL, 547 | radius=0.2, 548 | ) 549 | 550 | self.play( 551 | ShowCreation(curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 552 | 553 | self.play(ReplacementTransform(C1, C1_4), 554 | ReplacementTransform(curcurCircle1, 555 | curcurCircle1_4.move_arc_center_to(self.coords_to_point(4, 1)))) 556 | 557 | self.play(ReplacementTransform(C1_4, C4), 558 | ReplacementTransform(curcurCircle1_4, 559 | curcurCircle4.move_arc_center_to(self.coords_to_point(4, 4)))) 560 | # self.play(FadeOut(C4)) 561 | 562 | curcurCircle1 = Circle( 563 | color=BLUE, 564 | radius=0.2, 565 | ) 566 | curcurCircle1_3 = Circle( 567 | color=ORANGE, 568 | radius=0.2, 569 | ) 570 | curcurCircle3 = Circle( 571 | color=ORANGE, 572 | radius=0.2, 573 | ) 574 | 575 | self.play(ShowCreation(C1b), 576 | ShowCreation(curcurCircle1.move_arc_center_to( 577 | self.coords_to_point(0, 1)))) 578 | self.play(ReplacementTransform(C1b, C1_3), ReplacementTransform( 579 | curcurCircle1, 580 | curcurCircle1_3.move_arc_center_to(self.coords_to_point(5, 1)))) 581 | self.play(ReplacementTransform(C1_3, C3), ReplacementTransform( 582 | curcurCircle1_3, 583 | curcurCircle3.move_arc_center_to(self.coords_to_point(5, 3))) 584 | ) 585 | 586 | curcurCircle1 = Circle( 587 | color=BLUE, 588 | radius=0.2, 589 | ) 590 | 591 | curcurCircle1_2 = Circle( 592 | color=PURPLE, 593 | radius=0.2, 594 | ) 595 | 596 | curcurCircle2 = Circle( 597 | color=PURPLE, 598 | radius=0.2, 599 | ) 600 | 601 | self.play(ShowCreation(C1c), ShowCreation( 602 | curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 603 | self.play(ReplacementTransform(C1c, C1_2), ReplacementTransform( 604 | curcurCircle1, 605 | curcurCircle1_2.move_arc_center_to(self.coords_to_point(9, 1)))) 606 | self.play(ReplacementTransform(C1_2, C2), ReplacementTransform( 607 | curcurCircle1_2, 608 | curcurCircle2.move_arc_center_to(self.coords_to_point(9, 2)))) 609 | # self.play(FadeOut(C2)) 610 | 611 | curcurCircle1 = Circle( 612 | color=BLUE, 613 | radius=0.2, 614 | ) 615 | 616 | self.play(ShowCreation(C1d), 617 | ShowCreation( 618 | curcurCircle1.move_arc_center_to(self.coords_to_point(0, 1)))) 619 | 620 | self.wait(1) 621 | 622 | self.play( 623 | ReplacementTransform(C1d, C), 624 | ReplacementTransform(C2, C), 625 | ReplacementTransform(C3, C), 626 | ReplacementTransform(C4, C), 627 | FadeOut(curcurCircle1), 628 | FadeOut(curcurCircle2), 629 | FadeOut(curcurCircle3), 630 | FadeOut(curcurCircle4), 631 | ) 632 | 633 | self.wait(1) 634 | 635 | 636 | class fAndGConvolution_syn(GraphScene): 637 | CONFIG = { 638 | "x_min": -1, 639 | "x_max": 20, 640 | "y_min": 0, 641 | "y_max": 12, 642 | "graph_origin": 3 * DOWN + 4 * LEFT, 643 | 644 | "x_tick_frequency": 19, 645 | "y_tick_frequency": 10, 646 | "x_leftmost_tick": 0, 647 | 648 | "x_labeled_nums": [0, 4, 5, 9, 10, 14, 15, 19], 649 | "x_axis_label": "$x$", 650 | 651 | "y_labeled_nums": [1, 2, 3, 4, 10], 652 | "y_axis_label": "$y$", 653 | "exclude_zero_label": False, 654 | 655 | # to switch background 656 | "axes_color": black_white[mode], 657 | "label_color": black_white[mode], 658 | "label_nums_color": black_white[mode], 659 | "camera_config": {"background_color": black_white[1-mode]}, 660 | } 661 | 662 | def _draw(self, _shape, local_x, local_y): 663 | _shape.move_arc_center_to([self.coords_to_point(local_x, local_y)[ 664 | 0], self.coords_to_point(local_x, local_y)[1], 0]) 665 | return ShowCreation(_shape) 666 | 667 | def drawCircle(self, local_x, local_y): 668 | _circle = Circle( 669 | color=GREEN, 670 | # fill_color=WHITE, 671 | fill_color=black_white[1-mode], 672 | fill_opacity=1.0, 673 | radius=0.15, 674 | ) 675 | return self._draw(_circle, local_x, local_y) 676 | 677 | def drawDot(self, local_x, local_y): 678 | _dot = g2 = Dot( 679 | color=GREEN, 680 | radius=0.15, 681 | ) 682 | return self._draw(_dot, local_x, local_y) 683 | 684 | def zero(self, t): 685 | return 0 686 | 687 | def candle1(self, t): 688 | return 1 * CandleAxes.candle(self, t) 689 | 690 | def candle1_4(self, t): 691 | return CandleAxes.candle(self, t - 4) 692 | 693 | def candle4(self, t): 694 | return 4 * CandleAxes.candle(self, t - 4) 695 | 696 | def candle3(self, t): 697 | return 3 * CandleAxes.candle(self, t - 5) 698 | 699 | def candle1_3(self, t): 700 | return CandleAxes.candle(self, t - 5) 701 | 702 | def candle2(self, t): 703 | return 2 * CandleAxes.candle(self, t - 9) 704 | 705 | def candle1_2(self, t): 706 | return CandleAxes.candle(self, t - 9) 707 | 708 | def candle_sum(self, t): 709 | return self.candle1(t) + \ 710 | self.candle2(t) + \ 711 | self.candle3(t) + \ 712 | self.candle4(t) 713 | 714 | def construct(self): 715 | self.setup_axes(animate=True) 716 | 717 | self.wait(1) 718 | 719 | lighten_nb = self.get_graph(self.zero, GREEN, x_min=-2, x_max=21) 720 | 721 | C1 = self.get_graph( 722 | self.candle1, BLUE, x_min=-2, x_max=21) 723 | 724 | C1b = self.get_graph( 725 | self.candle1, BLUE, x_min=-2, x_max=21) 726 | 727 | C1c = self.get_graph( 728 | self.candle1, BLUE, x_min=-2, x_max=21) 729 | 730 | C1d = self.get_graph( 731 | self.candle1, BLUE, x_min=-2, x_max=21) 732 | 733 | C2 = self.get_graph( 734 | self.candle2, PURPLE, x_min=-2, x_max=21) 735 | 736 | C3 = self.get_graph( 737 | self.candle3, ORANGE, x_min=-2, x_max=21) 738 | 739 | C4 = self.get_graph( 740 | self.candle4, TEAL, x_min=-2, x_max=21) 741 | 742 | C1_2 = self.get_graph( 743 | self.candle1_2, PURPLE, x_min=-2, x_max=21) 744 | 745 | C1_3 = self.get_graph( 746 | self.candle1_3, ORANGE, x_min=-2, x_max=21) 747 | 748 | C1_4 = self.get_graph( 749 | self.candle1_4, TEAL, x_min=-2, x_max=21) 750 | 751 | C = self.get_graph( 752 | self.candle_sum, RED, x_min=-2, x_max=21) 753 | 754 | curcurCircle1b = Circle( 755 | color=BLUE, 756 | radius=0.2, 757 | ) 758 | curcurCircle1_4 = Circle( 759 | color=TEAL, 760 | radius=0.2, 761 | ) 762 | curcurCircle4 = Circle( 763 | color=TEAL, 764 | radius=0.2, 765 | ) 766 | 767 | curcurCircle1c = Circle( 768 | color=BLUE, 769 | radius=0.2, 770 | ) 771 | curcurCircle1_3 = Circle( 772 | color=ORANGE, 773 | radius=0.2, 774 | ) 775 | curcurCircle3 = Circle( 776 | color=ORANGE, 777 | radius=0.2, 778 | ) 779 | 780 | curcurCircle1d = Circle( 781 | color=BLUE, 782 | radius=0.2, 783 | ) 784 | 785 | curcurCircle1_2 = Circle( 786 | color=PURPLE, 787 | radius=0.2, 788 | ) 789 | 790 | curcurCircle2 = Circle( 791 | color=PURPLE, 792 | radius=0.2, 793 | ) 794 | 795 | curcurCircle1a = Circle( 796 | color=BLUE, 797 | radius=0.2, 798 | ) 799 | 800 | self.play(ShowCreation(lighten_nb), 801 | ShowCreation(C1b), 802 | ShowCreation(C1c), 803 | ShowCreation(C1d), 804 | self.drawCircle(0, 0), self.drawDot(0, 1), 805 | self.drawCircle(4, 0), self.drawDot(4, 4), 806 | self.drawCircle(5, 0), self.drawDot(5, 3), 807 | self.drawCircle(9, 0), self.drawDot(9, 2), 808 | ShowCreation(C1) 809 | ) 810 | 811 | self.wait(1) 812 | 813 | self.play( 814 | ShowCreation(curcurCircle1b.move_arc_center_to( 815 | self.coords_to_point(0, 1))), 816 | ShowCreation(curcurCircle1c.move_arc_center_to( 817 | self.coords_to_point(0, 1))), 818 | ShowCreation(curcurCircle1d.move_arc_center_to( 819 | self.coords_to_point(0, 1))), 820 | ShowCreation(curcurCircle1a.move_arc_center_to( 821 | self.coords_to_point(0, 1)))) 822 | 823 | self.play(ReplacementTransform(C1, C1_4), 824 | ReplacementTransform(curcurCircle1b, 825 | curcurCircle1_4.move_arc_center_to(self.coords_to_point(4, 1))), 826 | ReplacementTransform(C1b, C1_3), 827 | ReplacementTransform(curcurCircle1c, 828 | curcurCircle1_3.move_arc_center_to(self.coords_to_point(5, 1))), 829 | ReplacementTransform(C1c, C1_2), 830 | ReplacementTransform(curcurCircle1d, 831 | curcurCircle1_2.move_arc_center_to(self.coords_to_point(9, 1)))) 832 | 833 | self.play(ReplacementTransform(C1_4, C4), 834 | ReplacementTransform(curcurCircle1_4, 835 | curcurCircle4.move_arc_center_to(self.coords_to_point(4, 4))), 836 | ReplacementTransform(C1_3, C3), 837 | ReplacementTransform(curcurCircle1_3, 838 | curcurCircle3.move_arc_center_to(self.coords_to_point(5, 3))), 839 | ReplacementTransform(C1_2, C2), 840 | ReplacementTransform(curcurCircle1_2, 841 | curcurCircle2.move_arc_center_to(self.coords_to_point(9, 2)))) 842 | 843 | self.play( 844 | ReplacementTransform(C1d, C), 845 | ReplacementTransform(C2, C), 846 | ReplacementTransform(C3, C), 847 | ReplacementTransform(C4, C), 848 | FadeOut(curcurCircle1a), 849 | FadeOut(curcurCircle2), 850 | FadeOut(curcurCircle3), 851 | FadeOut(curcurCircle4), 852 | ) 853 | 854 | self.wait(1) 855 | 856 | 857 | class CandleAxesScaling(GraphScene): 858 | CONFIG = { 859 | "x_min": -1, 860 | "x_max": 11, 861 | "y_min": 0, 862 | "y_max": 1.2, 863 | "graph_origin": 3 * DOWN + 4 * LEFT, 864 | 865 | "x_tick_frequency": 5, 866 | "y_tick_frequency": 0.5, 867 | "x_leftmost_tick": 0, 868 | # "x_axis_width": 8, 869 | 870 | # "x_labeled_nums": [0, 2, 4, 6, 8, 10], 871 | "x_labeled_nums": [0, 5, 10], 872 | "x_axis_label": "$t$ (min)", 873 | 874 | "y_labeled_nums": [1], 875 | "y_axis_label": "$m$ (la)", 876 | "exclude_zero_label": False, 877 | 878 | # to switch background 879 | "axes_color": black_white[mode], 880 | "label_color": black_white[mode], 881 | "label_nums_color": black_white[mode], 882 | "camera_config": {"background_color": black_white[1-mode]}, 883 | } 884 | 885 | def candle(self, t): 886 | if t <= 0: 887 | return 1 888 | elif t <= 10: 889 | return 1 - (0.1 * t) 890 | else: 891 | return 0 892 | 893 | def candle2(self, t): 894 | return 1.15 * self.candle(t) 895 | 896 | def candle8(self, t): 897 | return 0.8 * self.candle(t) 898 | 899 | def candleFake(self, t): 900 | return .5 * self.candle(t) 901 | 902 | def candleReal(self, t): 903 | if t <= 0: 904 | return 0.5 905 | elif t <= 5: 906 | return 0.5 - (0.1 * t) 907 | else: 908 | return 0 909 | 910 | def construct(self): 911 | self.setup_axes(animate=True) 912 | c1 = self.get_graph(self.candle, BLUE, x_min=-2, x_max=12) 913 | c2 = self.get_graph(self.candle2, BLUE, x_min=-2, x_max=12) 914 | c8 = self.get_graph(self.candle8, BLUE, x_min=-2, x_max=12) 915 | self.play(ShowCreation(c1)) 916 | self.wait(1) 917 | self.play(ReplacementTransform(c1, c2)) 918 | self.play(ReplacementTransform(c2, c8)) 919 | c1 = self.get_graph(self.candle, BLUE, x_min=-2, x_max=12) 920 | self.play(ReplacementTransform(c8, c1)) 921 | 922 | self.wait(1) 923 | cT = self.get_graph(self.candleReal, GREEN, x_min=-2, x_max=12) 924 | cF = self.get_graph(self.candleFake, RED, x_min=-2, x_max=12) 925 | 926 | self.play(ShowCreation(cT)) 927 | self.wait(1) 928 | self.play(ReplacementTransform(cT, cF), ReplacementTransform(c1, cF)) 929 | self.wait(1) 930 | -------------------------------------------------------------------------------- /BV1JX4y1K7Dr/Manim Animations/graph_scene.py: -------------------------------------------------------------------------------- 1 | import itertools as it 2 | 3 | from manimlib.animation.creation import Write, DrawBorderThenFill, ShowCreation 4 | from manimlib.animation.transform import Transform 5 | from manimlib.animation.update import UpdateFromAlphaFunc 6 | from manimlib.constants import * 7 | from manimlib.mobject.functions import ParametricFunction 8 | from manimlib.mobject.geometry import Line 9 | from manimlib.mobject.geometry import Rectangle 10 | from manimlib.mobject.geometry import RegularPolygon 11 | from manimlib.mobject.number_line import NumberLine 12 | from manimlib.mobject.svg.tex_mobject import TexMobject 13 | from manimlib.mobject.svg.tex_mobject import TextMobject 14 | from manimlib.mobject.types.vectorized_mobject import VGroup 15 | from manimlib.mobject.types.vectorized_mobject import VectorizedPoint 16 | from manimlib.scene.scene import Scene 17 | from manimlib.utils.bezier import interpolate 18 | from manimlib.utils.color import color_gradient 19 | from manimlib.utils.color import invert_color 20 | from manimlib.utils.space_ops import angle_of_vector 21 | 22 | # TODO, this should probably reimplemented entirely, especially so as to 23 | # better reuse code from mobject/coordinate_systems. 24 | # Also, I really dislike how the configuration is set up, this 25 | # is way too messy to work with. 26 | 27 | 28 | class GraphScene(Scene): 29 | CONFIG = { 30 | "x_min": -1, 31 | "x_max": 10, 32 | "x_axis_width": 9, 33 | "x_tick_frequency": 1, 34 | "x_leftmost_tick": None, # Change if different from x_min 35 | "x_labeled_nums": None, 36 | "x_axis_label": "$x$", 37 | "y_min": -1, 38 | "y_max": 10, 39 | "y_axis_height": 6, 40 | "y_tick_frequency": 1, 41 | "y_bottom_tick": None, # Change if different from y_min 42 | "y_labeled_nums": None, 43 | "y_axis_label": "$y$", 44 | "axes_color": GREY, 45 | "graph_origin": 2.5 * DOWN + 4 * LEFT, 46 | "exclude_zero_label": True, 47 | "default_graph_colors": [BLUE, GREEN, YELLOW], 48 | "default_derivative_color": GREEN, 49 | "default_input_color": YELLOW, 50 | "default_riemann_start_color": BLUE, 51 | "default_riemann_end_color": GREEN, 52 | "area_opacity": 0.8, 53 | "num_rects": 50, 54 | } 55 | 56 | def setup(self): 57 | """ 58 | This method is used internally by Manim 59 | to set up the scene for proper use. 60 | """ 61 | self.default_graph_colors_cycle = it.cycle(self.default_graph_colors) 62 | 63 | self.left_T_label = VGroup() 64 | self.left_v_line = VGroup() 65 | self.right_T_label = VGroup() 66 | self.right_v_line = VGroup() 67 | 68 | def setup_axes(self, animate=False): 69 | """ 70 | This method sets up the axes of the graph. 71 | 72 | Parameters 73 | ---------- 74 | animate (bool=False) 75 | Whether or not to animate the setting up of the Axes. 76 | """ 77 | # TODO, once eoc is done, refactor this to be less redundant. 78 | x_num_range = float(self.x_max - self.x_min) 79 | self.space_unit_to_x = self.x_axis_width / x_num_range 80 | if self.x_labeled_nums is None: 81 | self.x_labeled_nums = [] 82 | if self.x_leftmost_tick is None: 83 | self.x_leftmost_tick = self.x_min 84 | x_axis = NumberLine( 85 | x_min=self.x_min, 86 | x_max=self.x_max, 87 | unit_size=self.space_unit_to_x, 88 | tick_frequency=self.x_tick_frequency, 89 | leftmost_tick=self.x_leftmost_tick, 90 | numbers_with_elongated_ticks=self.x_labeled_nums, 91 | color=self.axes_color, 92 | # Added this line 93 | decimal_number_config={"color": self.label_nums_color} 94 | ) 95 | x_axis.shift(self.graph_origin - x_axis.number_to_point(0)) 96 | if len(self.x_labeled_nums) > 0: 97 | if self.exclude_zero_label: 98 | self.x_labeled_nums = [ 99 | x for x in self.x_labeled_nums if x != 0] 100 | x_axis.add_numbers(*self.x_labeled_nums) 101 | if self.x_axis_label: 102 | x_label = TextMobject(self.x_axis_label) 103 | 104 | # Added this line 105 | x_label.set_color(self.label_color) 106 | x_label.next_to( 107 | x_axis.get_tick_marks(), UP + RIGHT, 108 | buff=SMALL_BUFF 109 | ) 110 | x_label.shift_onto_screen() 111 | x_axis.add(x_label) 112 | self.x_axis_label_mob = x_label 113 | y_num_range = float(self.y_max - self.y_min) 114 | self.space_unit_to_y = self.y_axis_height / y_num_range 115 | 116 | if self.y_labeled_nums is None: 117 | self.y_labeled_nums = [] 118 | if self.y_bottom_tick is None: 119 | self.y_bottom_tick = self.y_min 120 | y_axis = NumberLine( 121 | x_min=self.y_min, 122 | x_max=self.y_max, 123 | unit_size=self.space_unit_to_y, 124 | tick_frequency=self.y_tick_frequency, 125 | leftmost_tick=self.y_bottom_tick, 126 | numbers_with_elongated_ticks=self.y_labeled_nums, 127 | color=self.axes_color, 128 | line_to_number_vect=LEFT, 129 | label_direction=LEFT, 130 | # yongze added this line 131 | decimal_number_config={"color": self.label_nums_color}, 132 | ) 133 | y_axis.shift(self.graph_origin - y_axis.number_to_point(0)) 134 | y_axis.rotate(np.pi / 2, about_point=y_axis.number_to_point(0)) 135 | if len(self.y_labeled_nums) > 0: 136 | if self.exclude_zero_label: 137 | self.y_labeled_nums = [ 138 | y for y in self.y_labeled_nums if y != 0] 139 | y_axis.add_numbers(*self.y_labeled_nums) 140 | if self.y_axis_label: 141 | y_label = TextMobject(self.y_axis_label) 142 | # yongze Added this line 143 | y_label.set_color(self.label_color) 144 | y_label.next_to( 145 | y_axis.get_corner(UP + RIGHT), UP + RIGHT, 146 | buff=SMALL_BUFF 147 | ) 148 | y_label.shift_onto_screen() 149 | y_axis.add(y_label) 150 | self.y_axis_label_mob = y_label 151 | 152 | if animate: 153 | self.play(Write(VGroup(x_axis, y_axis))) 154 | else: 155 | self.add(x_axis, y_axis) 156 | self.x_axis, self.y_axis = self.axes = VGroup(x_axis, y_axis) 157 | self.default_graph_colors = it.cycle(self.default_graph_colors) 158 | 159 | def coords_to_point(self, x, y): 160 | """ 161 | The graph is smaller than the scene. 162 | Because of this, coordinates in the scene don't map 163 | to coordinates on the graph. 164 | This method returns a scaled coordinate for the graph, 165 | given cartesian coordinates that correspond to the scene.. 166 | 167 | Parameters 168 | ---------- 169 | x : (int,float) 170 | The x value 171 | 172 | y : (int,float) 173 | The y value 174 | 175 | Returns 176 | ------- 177 | np.ndarray 178 | The array of the coordinates. 179 | """ 180 | assert(hasattr(self, "x_axis") and hasattr(self, "y_axis")) 181 | result = self.x_axis.number_to_point(x)[0] * RIGHT 182 | result += self.y_axis.number_to_point(y)[1] * UP 183 | return result 184 | 185 | def point_to_coords(self, point): 186 | """ 187 | The scene is smaller than the graph. 188 | 189 | Because of this, coordinates in the graph don't map 190 | to coordinates on the scene. 191 | 192 | This method returns a scaled coordinate for the scene, 193 | given coordinates that correspond to the graph. 194 | 195 | Parameters 196 | ---------- 197 | point (np.ndarray) 198 | The point on the graph. 199 | 200 | Returns 201 | ------- 202 | tuple 203 | The coordinates on the scene. 204 | """ 205 | return (self.x_axis.point_to_number(point), 206 | self.y_axis.point_to_number(point)) 207 | 208 | def get_graph( 209 | self, func, 210 | color=None, 211 | x_min=None, 212 | x_max=None, 213 | **kwargs 214 | ): 215 | """ 216 | This method gets a curve to plot on the graph. 217 | 218 | Parameters 219 | ---------- 220 | func : function 221 | The function to plot. It's return value should be 222 | the y-coordinate for a given x-coordinate 223 | 224 | color : str 225 | The string of the RGB color of the curve. in Hexadecimal representation. 226 | 227 | x_min : (Union[int,float]) 228 | The lower x_value from which to plot the curve. 229 | 230 | x_max : (Union[int,float]) 231 | The higher x_value until which to plot the curve. 232 | 233 | **kwargs: 234 | Any valid keyword arguments of ParametricFunction. 235 | 236 | Return 237 | ------ 238 | ParametricFunction 239 | The Parametric Curve for the function passed. 240 | 241 | """ 242 | if color is None: 243 | color = next(self.default_graph_colors_cycle) 244 | if x_min is None: 245 | x_min = self.x_min 246 | if x_max is None: 247 | x_max = self.x_max 248 | 249 | def parameterized_function(alpha): 250 | x = interpolate(x_min, x_max, alpha) 251 | y = func(x) 252 | if not np.isfinite(y): 253 | y = self.y_max 254 | return self.coords_to_point(x, y) 255 | 256 | graph = ParametricFunction( 257 | parameterized_function, 258 | color=color, 259 | **kwargs 260 | ) 261 | graph.underlying_function = func 262 | return graph 263 | 264 | def input_to_graph_point(self, x, graph): 265 | """ 266 | This method returns a coordinate on the curve 267 | given an x_value and a the graoh-curve for which 268 | the corresponding y value should be found. 269 | 270 | Parameters 271 | ---------- 272 | x (Union[int, float]) 273 | The x value for which to find the y value. 274 | 275 | graph ParametricFunction 276 | The ParametricFunction object on which 277 | the x and y value lie. 278 | 279 | Returns 280 | ------- 281 | numpy.nparray 282 | The array of the coordinates on the graph. 283 | """ 284 | return self.coords_to_point(x, graph.underlying_function(x)) 285 | 286 | def angle_of_tangent(self, x, graph, dx=0.01): 287 | """ 288 | Returns the angle to the x axis of the tangent 289 | to the plotted curve at a particular x-value. 290 | 291 | Parameters 292 | ---------- 293 | x (Union[int, float]) 294 | The x value at which the tangent must touch the curve. 295 | 296 | graph ParametricFunction 297 | The ParametricFunction for which to calculate the tangent. 298 | 299 | dx (Union(float, int =0.01)) 300 | The small change in x with which a small change in y 301 | will be compared in order to obtain the tangent. 302 | 303 | Returns 304 | ------- 305 | float 306 | The angle of the tangent with the x axis. 307 | """ 308 | vect = self.input_to_graph_point( 309 | x + dx, graph) - self.input_to_graph_point(x, graph) 310 | return angle_of_vector(vect) 311 | 312 | def slope_of_tangent(self, *args, **kwargs): 313 | """ 314 | Returns the slople of the tangent to the plotted curve 315 | at a particular x-value. 316 | 317 | Parameters 318 | ---------- 319 | x (Union[int, float]) 320 | The x value at which the tangent must touch the curve. 321 | 322 | graph ParametricFunction 323 | The ParametricFunction for which to calculate the tangent. 324 | 325 | dx (Union(float, int =0.01)) 326 | The small change in x with which a small change in y 327 | will be compared in order to obtain the tangent. 328 | 329 | Returns 330 | ------- 331 | float 332 | The slope of the tangent with the x axis. 333 | """ 334 | return np.tan(self.angle_of_tangent(*args, **kwargs)) 335 | 336 | def get_derivative_graph(self, graph, dx=0.01, **kwargs): 337 | """ 338 | Returns the curve of the derivative of the passed 339 | graph. 340 | 341 | Parameters 342 | ---------- 343 | graph (ParametricFunction) 344 | The graph for which the derivative must be found. 345 | 346 | dx (Union(float, int =0.01)) 347 | The small change in x with which a small change in y 348 | will be compared in order to obtain the derivative. 349 | 350 | **kwargs 351 | Any valid keyword argument of ParametricFunction 352 | 353 | Returns 354 | ------- 355 | ParametricFuncion 356 | The curve of the derivative. 357 | """ 358 | if "color" not in kwargs: 359 | kwargs["color"] = self.default_derivative_color 360 | 361 | def deriv(x): 362 | return self.slope_of_tangent(x, graph, dx) / self.space_unit_to_y 363 | return self.get_graph(deriv, **kwargs) 364 | 365 | def get_graph_label( 366 | self, 367 | graph, 368 | label="f(x)", 369 | x_val=None, 370 | direction=RIGHT, 371 | buff=MED_SMALL_BUFF, 372 | color=None, 373 | ): 374 | """ 375 | This method returns a properly positioned label for the passed graph, 376 | styled with the passed parameters. 377 | 378 | Parameters 379 | ---------- 380 | graph : ParametricFunction 381 | The curve of the function plotted. 382 | 383 | label : str = "f(x)" 384 | The label for the function's curve. 385 | 386 | x_val : Union[float, int] 387 | The x_value with which the label should be aligned. 388 | 389 | direction : Union[np.ndarray,list,tuple]=RIGHT 390 | The position, relative to the curve that the label will be at. 391 | e.g LEFT, RIGHT 392 | 393 | buff : Union[float, int] 394 | The buffer space between the curve and the label 395 | 396 | color : str 397 | The color of the label. 398 | 399 | Returns 400 | ------- 401 | TexMobject 402 | The LaTeX of the passed 'label' parameter 403 | 404 | """ 405 | label = TexMobject(label) 406 | color = color or graph.get_color() 407 | label.set_color(color) 408 | if x_val is None: 409 | # Search from right to left 410 | for x in np.linspace(self.x_max, self.x_min, 100): 411 | point = self.input_to_graph_point(x, graph) 412 | if point[1] < FRAME_Y_RADIUS: 413 | break 414 | x_val = x 415 | label.next_to( 416 | self.input_to_graph_point(x_val, graph), 417 | direction, 418 | buff=buff 419 | ) 420 | label.shift_onto_screen() 421 | return label 422 | 423 | def get_riemann_rectangles( 424 | self, 425 | graph, 426 | x_min=None, 427 | x_max=None, 428 | dx=0.1, 429 | input_sample_type="left", 430 | stroke_width=1, 431 | stroke_color=BLACK, 432 | fill_opacity=1, 433 | start_color=None, 434 | end_color=None, 435 | show_signed_area=True, 436 | width_scale_factor=1.001 437 | ): 438 | """ 439 | This method returns the VGroup() of the Riemann Rectangles for 440 | a particular curve. 441 | 442 | Parameters 443 | ---------- 444 | graph (ParametricFunction) 445 | The graph whose area needs to be approximated 446 | by the Riemann Rectangles. 447 | 448 | x_min Union[int,float] 449 | The lower bound from which to start adding rectangles 450 | 451 | x_max Union[int,float] 452 | The upper bound where the rectangles stop. 453 | 454 | dx Union[int,float] 455 | The smallest change in x-values that is 456 | considered significant. 457 | 458 | input_sample_type str 459 | Can be any of "left", "right" or "center 460 | 461 | stroke_width : Union[int, float] 462 | The stroke_width of the border of the rectangles. 463 | 464 | stroke_color : str 465 | The string of hex colour of the rectangle's border. 466 | 467 | fill_opacity Union[int, float] 468 | The opacity of the rectangles. 469 | 470 | start_color : str, 471 | The hex starting colour for the rectangles, 472 | this will, if end_color is a different colour, 473 | make a nice gradient. 474 | 475 | end_color : str, 476 | The hex ending colour for the rectangles, 477 | this will, if start_color is a different colour, 478 | make a nice gradient. 479 | 480 | show_signed_area : bool (True) 481 | Whether or not to indicate -ve area if curve dips below 482 | x-axis. 483 | 484 | width_scale_factor : Union[int, float] 485 | How much the width of the rectangles are scaled by when transforming. 486 | 487 | Returns 488 | ------- 489 | VGroup 490 | A VGroup containing the Riemann Rectangles. 491 | 492 | """ 493 | x_min = x_min if x_min is not None else self.x_min 494 | x_max = x_max if x_max is not None else self.x_max 495 | if start_color is None: 496 | start_color = self.default_riemann_start_color 497 | if end_color is None: 498 | end_color = self.default_riemann_end_color 499 | rectangles = VGroup() 500 | x_range = np.arange(x_min, x_max, dx) 501 | colors = color_gradient([start_color, end_color], len(x_range)) 502 | for x, color in zip(x_range, colors): 503 | if input_sample_type == "left": 504 | sample_input = x 505 | elif input_sample_type == "right": 506 | sample_input = x + dx 507 | elif input_sample_type == "center": 508 | sample_input = x + 0.5 * dx 509 | else: 510 | raise Exception("Invalid input sample type") 511 | graph_point = self.input_to_graph_point(sample_input, graph) 512 | points = VGroup(*list(map(VectorizedPoint, [ 513 | self.coords_to_point(x, 0), 514 | self.coords_to_point(x + width_scale_factor * dx, 0), 515 | graph_point 516 | ]))) 517 | 518 | rect = Rectangle() 519 | rect.replace(points, stretch=True) 520 | if graph_point[1] < self.graph_origin[1] and show_signed_area: 521 | fill_color = invert_color(color) 522 | else: 523 | fill_color = color 524 | rect.set_fill(fill_color, opacity=fill_opacity) 525 | rect.set_stroke(stroke_color, width=stroke_width) 526 | rectangles.add(rect) 527 | return rectangles 528 | 529 | def get_riemann_rectangles_list( 530 | self, 531 | graph, 532 | n_iterations, 533 | max_dx=0.5, 534 | power_base=2, 535 | stroke_width=1, 536 | **kwargs 537 | ): 538 | """ 539 | This method returns a list of multiple VGroups of Riemann 540 | Rectangles. The inital VGroups are relatively inaccurate, 541 | but the closer you get to the end the more accurate the Riemann 542 | rectangles become 543 | 544 | Parameters 545 | ---------- 546 | graph (ParametricFunction) 547 | The graph whose area needs to be approximated 548 | by the Riemann Rectangles. 549 | 550 | n_iterations, 551 | The number of VGroups of successive accuracy that are needed. 552 | 553 | max_dx Union[int,float] 554 | The maximum change in x between two VGroups of Riemann Rectangles 555 | 556 | power_base Union[int,float=2] 557 | 558 | stroke_width : Union[int, float] 559 | The stroke_width of the border of the rectangles. 560 | 561 | **kwargs 562 | Any valid keyword arguments of get_riemann_rectangles. 563 | 564 | Returns 565 | ------- 566 | list 567 | The list of Riemann Rectangles of increasing accuracy. 568 | """ 569 | return [ 570 | self.get_riemann_rectangles( 571 | graph=graph, 572 | dx=float(max_dx) / (power_base**n), 573 | stroke_width=float(stroke_width) / (power_base**n), 574 | **kwargs 575 | ) 576 | for n in range(n_iterations) 577 | ] 578 | 579 | def get_area(self, graph, t_min, t_max): 580 | """ 581 | Returns a VGroup of Riemann rectangles 582 | sufficiently small enough to visually 583 | approximate the area under the graph passed. 584 | 585 | Parameters 586 | ---------- 587 | graph (ParametricFunction) 588 | The graph/curve for which the area needs to be gotten. 589 | 590 | t_min Union[int, float] 591 | The lower bound of x from which to approximate the area. 592 | 593 | t_max Union[int, float] 594 | The upper bound of x until which the area must be approximated. 595 | 596 | Returns 597 | ------- 598 | VGroup 599 | The VGroup containing the Riemann Rectangles. 600 | """ 601 | numerator = max(t_max - t_min, 0.0001) 602 | dx = float(numerator) / self.num_rects 603 | return self.get_riemann_rectangles( 604 | graph, 605 | x_min=t_min, 606 | x_max=t_max, 607 | dx=dx, 608 | stroke_width=0, 609 | ).set_fill(opacity=self.area_opacity) 610 | 611 | def transform_between_riemann_rects(self, curr_rects, new_rects, **kwargs): 612 | """ 613 | This method is used to transform between two VGroups of Riemann Rectangles, 614 | if they were obtained by get_riemann_rectangles or get_riemann_rectangles_list. 615 | No animation is returned, and the animation is directly played. 616 | 617 | Parameters 618 | ---------- 619 | curr_rects : VGroup 620 | The current Riemann Rectangles 621 | 622 | new_rects : VGroup 623 | The Riemann Rectangles to transform to. 624 | 625 | **kwargs 626 | added_anims 627 | Any other animations to play simultaneously. 628 | """ 629 | transform_kwargs = { 630 | "run_time": 2, 631 | "lag_ratio": 0.5 632 | } 633 | added_anims = kwargs.get("added_anims", []) 634 | transform_kwargs.update(kwargs) 635 | curr_rects.align_submobjects(new_rects) 636 | x_coords = set() # Keep track of new repetitions 637 | for rect in curr_rects: 638 | x = rect.get_center()[0] 639 | if x in x_coords: 640 | rect.set_fill(opacity=0) 641 | else: 642 | x_coords.add(x) 643 | self.play( 644 | Transform(curr_rects, new_rects, **transform_kwargs), 645 | *added_anims 646 | ) 647 | 648 | def get_vertical_line_to_graph( 649 | self, 650 | x, graph, 651 | line_class=Line, 652 | **line_kwargs 653 | ): 654 | """ 655 | This method returns a Vertical line from the x-axis to 656 | the corresponding point on the graph/curve. 657 | 658 | Parameters 659 | ---------- 660 | x Union[int,float] 661 | The x-value at which the line should be placed/calculated. 662 | 663 | graph (ParametricFunction) 664 | The graph on which the line should extend to. 665 | 666 | line_class (Line and similar) 667 | The type of line that should be used. 668 | Defaults to Line 669 | 670 | **line_kwargs 671 | Any valid keyword arguments of the object passed in "line_class" 672 | If line_class is Line, any valid keyword arguments of Line are allowed. 673 | 674 | Return 675 | ------ 676 | An object of type passed in "line_class" 677 | Defaults to Line 678 | """ 679 | if "color" not in line_kwargs: 680 | line_kwargs["color"] = graph.get_color() 681 | return line_class( 682 | self.coords_to_point(x, 0), 683 | self.input_to_graph_point(x, graph), 684 | **line_kwargs 685 | ) 686 | 687 | def get_vertical_lines_to_graph( 688 | self, graph, 689 | x_min=None, 690 | x_max=None, 691 | num_lines=20, 692 | **kwargs 693 | ): 694 | """ 695 | Obtains multiple lines from the x axis to the Graph/curve. 696 | 697 | Parameters 698 | ---------- 699 | graph (ParametricFunction) 700 | The graph on which the line should extend to. 701 | 702 | x_min (Union[int, float]) 703 | The lower bound from which lines can appear. 704 | 705 | x_max (Union[int, float]) 706 | The upper bound until which the lines can appear. 707 | 708 | num_lines (Union[int, float]) 709 | The number of lines (evenly spaced) 710 | that are needed. 711 | 712 | Returns 713 | ------- 714 | VGroup 715 | The VGroup of the evenly spaced lines. 716 | 717 | """ 718 | x_min = x_min or self.x_min 719 | x_max = x_max or self.x_max 720 | return VGroup(*[ 721 | self.get_vertical_line_to_graph(x, graph, **kwargs) 722 | for x in np.linspace(x_min, x_max, num_lines) 723 | ]) 724 | 725 | def get_secant_slope_group( 726 | self, 727 | x, graph, 728 | dx=None, 729 | dx_line_color=None, 730 | df_line_color=None, 731 | dx_label=None, 732 | df_label=None, 733 | include_secant_line=True, 734 | secant_line_color=None, 735 | secant_line_length=10, 736 | ): 737 | """ 738 | This method returns a VGroup of (two lines 739 | representing dx and df, the labels for dx and 740 | df, and the Secant to the Graph/curve at a 741 | particular x value. 742 | 743 | Parameters 744 | ---------- 745 | x (Union[float, int]) 746 | The x value at which the secant enters, and intersects 747 | the graph for the first time. 748 | 749 | graph (ParametricFunction) 750 | The curve/graph for which the secant must 751 | be found. 752 | 753 | dx (Union[float, int]) 754 | The change in x after which the secant exits. 755 | 756 | dx_line_color (str) 757 | The line color for the line that indicates the change in x. 758 | 759 | df_line_color (str) 760 | The line color for the line that indicates the change in y. 761 | 762 | dx_label (str) 763 | The label to be provided for the change in x. 764 | 765 | df_label (str) 766 | The label to be provided for the change in y. 767 | 768 | include_secant_line (bool=True) 769 | Whether or not to include the secant line in the graph, 770 | or just have the df and dx lines and labels. 771 | 772 | secant_line_color (str) 773 | The color of the secant line. 774 | 775 | secant_line_length (Union[float,int=10]) 776 | How long the secant line should be. 777 | 778 | Returns: 779 | -------- 780 | VGroup 781 | Resulting group is of the form VGroup( 782 | dx_line, 783 | df_line, 784 | dx_label, (if applicable) 785 | df_label, (if applicable) 786 | secant_line, (if applicable) 787 | ) 788 | with attributes of those names. 789 | """ 790 | kwargs = locals() 791 | kwargs.pop("self") 792 | group = VGroup() 793 | group.kwargs = kwargs 794 | 795 | dx = dx or float(self.x_max - self.x_min) / 10 796 | dx_line_color = dx_line_color or self.default_input_color 797 | df_line_color = df_line_color or graph.get_color() 798 | 799 | p1 = self.input_to_graph_point(x, graph) 800 | p2 = self.input_to_graph_point(x + dx, graph) 801 | interim_point = p2[0] * RIGHT + p1[1] * UP 802 | 803 | group.dx_line = Line( 804 | p1, interim_point, 805 | color=dx_line_color 806 | ) 807 | group.df_line = Line( 808 | interim_point, p2, 809 | color=df_line_color 810 | ) 811 | group.add(group.dx_line, group.df_line) 812 | 813 | labels = VGroup() 814 | if dx_label is not None: 815 | group.dx_label = TexMobject(dx_label) 816 | labels.add(group.dx_label) 817 | group.add(group.dx_label) 818 | if df_label is not None: 819 | group.df_label = TexMobject(df_label) 820 | labels.add(group.df_label) 821 | group.add(group.df_label) 822 | 823 | if len(labels) > 0: 824 | max_width = 0.8 * group.dx_line.get_width() 825 | max_height = 0.8 * group.df_line.get_height() 826 | if labels.get_width() > max_width: 827 | labels.set_width(max_width) 828 | if labels.get_height() > max_height: 829 | labels.set_height(max_height) 830 | 831 | if dx_label is not None: 832 | group.dx_label.next_to( 833 | group.dx_line, 834 | np.sign(dx) * DOWN, 835 | buff=group.dx_label.get_height() / 2 836 | ) 837 | group.dx_label.set_color(group.dx_line.get_color()) 838 | 839 | if df_label is not None: 840 | group.df_label.next_to( 841 | group.df_line, 842 | np.sign(dx) * RIGHT, 843 | buff=group.df_label.get_height() / 2 844 | ) 845 | group.df_label.set_color(group.df_line.get_color()) 846 | 847 | if include_secant_line: 848 | secant_line_color = secant_line_color or self.default_derivative_color 849 | group.secant_line = Line(p1, p2, color=secant_line_color) 850 | group.secant_line.scale_in_place( 851 | secant_line_length / group.secant_line.get_length() 852 | ) 853 | group.add(group.secant_line) 854 | 855 | return group 856 | 857 | def add_T_label(self, x_val, side=RIGHT, label=None, color=WHITE, animated=False, **kwargs): 858 | """ 859 | This method adds to the Scene: 860 | -- a Vertical line from the x-axis to the corresponding point on the graph/curve. 861 | -- a small vertical Triangle whose top point lies on the base of the vertical line 862 | -- a TexMobject to be a label for the Line and Triangle, at the bottom of the Triangle. 863 | The scene needs to have the graph have the identifier/variable name self.v_graph. 864 | 865 | Parameters 866 | ---------- 867 | x_val (Union[float, int]) 868 | The x value at which the secant enters, and intersects 869 | the graph for the first time. 870 | 871 | side (np.ndarray()) 872 | 873 | label (str) 874 | The label to give the vertline and triangle 875 | 876 | color (str) 877 | The hex color of the label. 878 | 879 | animated (bool=False) 880 | Whether or not to animate the addition of the T_label 881 | 882 | **kwargs 883 | Any valid keyword argument of a self.play call. 884 | """ 885 | triangle = RegularPolygon(n=3, start_angle=np.pi / 2) 886 | triangle.set_height(MED_SMALL_BUFF) 887 | triangle.move_to(self.coords_to_point(x_val, 0), UP) 888 | triangle.set_fill(color, 1) 889 | triangle.set_stroke(width=0) 890 | if label is None: 891 | T_label = TexMobject(self.variable_point_label, fill_color=color) 892 | else: 893 | T_label = TexMobject(label, fill_color=color) 894 | 895 | T_label.next_to(triangle, DOWN) 896 | v_line = self.get_vertical_line_to_graph( 897 | x_val, self.v_graph, 898 | color=YELLOW 899 | ) 900 | 901 | if animated: 902 | self.play( 903 | DrawBorderThenFill(triangle), 904 | ShowCreation(v_line), 905 | Write(T_label, run_time=1), 906 | **kwargs 907 | ) 908 | 909 | if np.all(side == LEFT): 910 | self.left_T_label_group = VGroup(T_label, triangle) 911 | self.left_v_line = v_line 912 | self.add(self.left_T_label_group, self.left_v_line) 913 | elif np.all(side == RIGHT): 914 | self.right_T_label_group = VGroup(T_label, triangle) 915 | self.right_v_line = v_line 916 | self.add(self.right_T_label_group, self.right_v_line) 917 | 918 | def get_animation_integral_bounds_change( 919 | self, 920 | graph, 921 | new_t_min, 922 | new_t_max, 923 | fade_close_to_origin=True, 924 | run_time=1.0 925 | ): 926 | """ 927 | This method requires a lot of prerequisites: 928 | self.area must be defined from self.get_area() 929 | self.left_v_line and self.right_v_line must be defined from self.get_v_line 930 | self.left_T_label_group and self.right_T_label_group must be defined from self.add_T_label 931 | 932 | This method will returna VGroup of new mobjects for each of those, when provided the graph/curve, 933 | the new t_min and t_max, the run_time and a bool stating whether or not to fade when close to 934 | the origin. 935 | 936 | Parameters 937 | ---------- 938 | graph (ParametricFunction) 939 | The graph for which this must be done. 940 | 941 | new_t_min (Union[int,float]) 942 | The new lower bound. 943 | 944 | new_t_max (Union[int,float]) 945 | The new upper bound. 946 | 947 | fade_close_to_origin (bool=True) 948 | Whether or not to fade when close to the origin. 949 | 950 | run_time (Union[int,float=1.0]) 951 | The run_time of the animation of this change. 952 | """ 953 | curr_t_min = self.x_axis.point_to_number(self.area.get_left()) 954 | curr_t_max = self.x_axis.point_to_number(self.area.get_right()) 955 | if new_t_min is None: 956 | new_t_min = curr_t_min 957 | if new_t_max is None: 958 | new_t_max = curr_t_max 959 | 960 | group = VGroup(self.area) 961 | group.add(self.left_v_line) 962 | group.add(self.left_T_label_group) 963 | group.add(self.right_v_line) 964 | group.add(self.right_T_label_group) 965 | 966 | def update_group(group, alpha): 967 | area, left_v_line, left_T_label, right_v_line, right_T_label = group 968 | t_min = interpolate(curr_t_min, new_t_min, alpha) 969 | t_max = interpolate(curr_t_max, new_t_max, alpha) 970 | new_area = self.get_area(graph, t_min, t_max) 971 | 972 | new_left_v_line = self.get_vertical_line_to_graph( 973 | t_min, graph 974 | ) 975 | new_left_v_line.set_color(left_v_line.get_color()) 976 | left_T_label.move_to(new_left_v_line.get_bottom(), UP) 977 | 978 | new_right_v_line = self.get_vertical_line_to_graph( 979 | t_max, graph 980 | ) 981 | new_right_v_line.set_color(right_v_line.get_color()) 982 | right_T_label.move_to(new_right_v_line.get_bottom(), UP) 983 | 984 | # Fade close to 0 985 | if fade_close_to_origin: 986 | if len(left_T_label) > 0: 987 | left_T_label[0].set_fill(opacity=min(1, np.abs(t_min))) 988 | if len(right_T_label) > 0: 989 | right_T_label[0].set_fill(opacity=min(1, np.abs(t_max))) 990 | 991 | Transform(area, new_area).update(1) 992 | Transform(left_v_line, new_left_v_line).update(1) 993 | Transform(right_v_line, new_right_v_line).update(1) 994 | return group 995 | 996 | return UpdateFromAlphaFunc(group, update_group, run_time=run_time) 997 | 998 | def animate_secant_slope_group_change( 999 | self, secant_slope_group, 1000 | target_dx=None, 1001 | target_x=None, 1002 | run_time=3, 1003 | added_anims=None, 1004 | **anim_kwargs 1005 | ): 1006 | """ 1007 | This method animates the change of the secant slope group from 1008 | the old secant slope group, into a new secant slope group. 1009 | 1010 | Parameters 1011 | ---------- 1012 | secant_slope_group (VGroup) 1013 | The old secant_slope_group 1014 | 1015 | target_dx Union[int, float] 1016 | The new dx value. 1017 | 1018 | target_x Union[int, float] 1019 | The new x value at which the secant should be. 1020 | 1021 | run_time Union[int,float=3] 1022 | The run time for this change when animated. 1023 | 1024 | added_anims 1025 | Any exta animations that should be played alongside. 1026 | 1027 | **anim_kwargs 1028 | Any valid kwargs of a self.play call. 1029 | 1030 | NOTE: At least one of target_dx and target_x should be not None. 1031 | """ 1032 | if target_dx is None and target_x is None: 1033 | raise Exception( 1034 | "At least one of target_x and target_dx must not be None") 1035 | if added_anims is None: 1036 | added_anims = [] 1037 | 1038 | start_dx = secant_slope_group.kwargs["dx"] 1039 | start_x = secant_slope_group.kwargs["x"] 1040 | if target_dx is None: 1041 | target_dx = start_dx 1042 | if target_x is None: 1043 | target_x = start_x 1044 | 1045 | def update_func(group, alpha): 1046 | dx = interpolate(start_dx, target_dx, alpha) 1047 | x = interpolate(start_x, target_x, alpha) 1048 | kwargs = dict(secant_slope_group.kwargs) 1049 | kwargs["dx"] = dx 1050 | kwargs["x"] = x 1051 | new_group = self.get_secant_slope_group(**kwargs) 1052 | group.become(new_group) 1053 | return group 1054 | 1055 | self.play( 1056 | UpdateFromAlphaFunc( 1057 | secant_slope_group, update_func, 1058 | run_time=run_time, 1059 | **anim_kwargs 1060 | ), 1061 | *added_anims 1062 | ) 1063 | secant_slope_group.kwargs["x"] = target_x 1064 | secant_slope_group.kwargs["dx"] = target_dx 1065 | -------------------------------------------------------------------------------- /BV1JX4y1K7Dr/Manim Animations/series.py: -------------------------------------------------------------------------------- 1 | from manimlib.imports import * 2 | 3 | mode = 0 # 1 for black background, 0 for white 4 | black_white = [BLACK, WHITE] 5 | 6 | 7 | class g(Scene): 8 | 9 | CONFIG = { 10 | "camera_config": {"background_color": black_white[1 - mode]}, 11 | 12 | 13 | } 14 | 15 | def construct(self): 16 | t1 = TexMobject( 17 | "g(x)") 18 | t1.set_color(BLUE) 19 | 20 | self.play(ShowCreation(t1)) 21 | self.wait(3) 22 | 23 | 24 | class f(Scene): 25 | 26 | CONFIG = { 27 | "camera_config": {"background_color": black_white[1 - mode]}, 28 | 29 | 30 | } 31 | 32 | def construct(self): 33 | t1 = TexMobject( 34 | "f(x)") 35 | t1.set_color(GREEN) 36 | 37 | self.play(ShowCreation(t1)) 38 | self.wait(3) 39 | 40 | 41 | class convolutionDef(Scene): 42 | 43 | CONFIG = { 44 | "camera_config": {"background_color": black_white[1 - mode]}, 45 | 46 | 47 | } 48 | 49 | def construct(self): 50 | t1 = TexMobject( 51 | "(f*g)(x)", "=\\int_{-\\infty}^{\\infty}", "f", "(t)", "g", "(x-t){\\rm d}t") 52 | t1.set_color(black_white[mode]) 53 | 54 | t1[0].set_color(RED) 55 | 56 | self.play(ShowCreation(t1)) 57 | self.wait(1) 58 | 59 | 60 | class convolutionDis(Scene): 61 | 62 | CONFIG = { 63 | "camera_config": {"background_color": black_white[1 - mode]}, 64 | 65 | 66 | } 67 | 68 | def construct(self): 69 | t1 = TexMobject( 70 | "f(x)&=\\begin{cases}1, & x=0\\\\4, & x=4\\\\3, & x=5\\\\2, & x=9\\\\0, &\\rm{otherwise}\\end{cases}\\\\g(x)&=\\begin{cases}-0.1x+1, & 0 < x < 1\\\\0, &\\rm{otherwise}\\end{cases}") 71 | t1.set_color(BLACK) 72 | 73 | self.play(ShowCreation(t1)) 74 | self.wait(1) 75 | -------------------------------------------------------------------------------- /BV1JX4y1K7Dr/markdown/Canon_formula.md: -------------------------------------------------------------------------------- 1 | 此式作废 2 | 3 | $$ 4 | \begin{align} 5 | &g(x)=\begin{cases}(\boldsymbol G)_{11}, & 0\le x <1\\ 6 | (\boldsymbol G)_{k2},&2\le k\le32\quad{\rm s.t.}\;\sum _{i=1}^{k-1}(\boldsymbol G)_{i1} \le x<\sum _{i=1}^{k}(\boldsymbol G)_{i1}\\ 7 | 0, & \text {否则}\end{cases}\\ 8 | 9 | &{\text {其中}\;}\boldsymbol G = \left[\begin{matrix}\boldsymbol G_{\rm part-a} \\ \boldsymbol G_{\rm part-b} \\\boldsymbol G_{\rm part-c} \end{matrix}\right]\in \mathbb R ^{32\times 2}{\;\rm 且}\\ 10 | 11 | &\boldsymbol G_{\rm part-a}=\left[\begin{matrix} 12 | 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 2 & 1 & 1 & 2\\ 13 | 0 & 2 & 4 & 0 & 0 & 2 & 4 & 0 & 4 & 5 & 7 & 4 & 5 & 7 \end{matrix}\right]^{\text T}\in \mathbb R ^{14\times 2},\\ 14 | 15 | &\boldsymbol G_{\rm part-b}=\left[\begin{matrix}0.5 & 0.5 & 0.5 & 0.5 & 1 & 1 & 0.5 & 0.5 & 0.5 & 0.5 & 1 & 1 \\7 & 9 & 7 & 5 & 4 & 0& 7 & 9 & 7 & 5 & 4 & 0 \end{matrix}\right]^{\text T}\in \mathbb R ^{12\times 2},\\ 16 | 17 | &\boldsymbol G_{\rm part-c}=\left[\begin{matrix} 1&1&2&1&1&2 \\ 18 | 2 & -5 & 0 & 2 & -5 & 0\end{matrix}\right]^{\text T}\in \mathbb R ^{6\times 2}.\end{align} 19 | $$ 20 | 21 | --- 22 | 23 | # 以此为准 24 | 25 | $$ 26 | \begin{align} 27 | &g(x)=\begin{cases} 28 | 29 | G_{1,2}, & 0\le x < 1\\ 30 | G_{k,2}, & k\in\mathbb Z_+,2\le k<32 \quad\text{s.t.}\quad G_{k-1,1}\le x { 2 | const background = document.querySelector('.background'); 3 | const loginForm = document.querySelector('.login-form'); 4 | 5 | // 处理表单提交 6 | loginForm.addEventListener('submit', (e) => { 7 | e.preventDefault(); 8 | const cardNumber = document.getElementById('cardNumber').value; 9 | const password = document.getElementById('password').value; 10 | 11 | // 这里可以添加验证逻辑 12 | if (cardNumber && password) { 13 | // 触发卡片出现的事件 14 | const event = new CustomEvent('loginSuccess'); 15 | document.dispatchEvent(event); 16 | 17 | // 淡出表单 18 | loginForm.classList.add('fade-out'); 19 | } 20 | }); 21 | }); -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErSanSan233/PersonalVideoScripts/624480cc4ab6ef6ac4a797d4877eb1bb715f5bd7/BV1YqZJYaEo5/milliniumMenu/front.png -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 3D卡片展示 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 | 17 |
18 | 19 |
20 |
21 | 22 |
赛车币:0
23 | 28 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 卡片正面 58 |
59 |
60 |
61 |
62 |
63 | 卡片背面-揭开 64 | 卡片背面-遮罩 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErSanSan233/PersonalVideoScripts/624480cc4ab6ef6ac4a797d4877eb1bb715f5bd7/BV1YqZJYaEo5/milliniumMenu/logo.png -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | // DOM元素和状态管理 3 | const container = document.querySelector('.container'); 4 | const card = document.querySelector('.card'); 5 | const cardInner = document.querySelector('.card-inner'); 6 | const frontOverlay = card.querySelector('.card-front .holographic-overlay'); 7 | let isHovering = false; 8 | let isFlipped = false; 9 | let rafId = null; 10 | let isVisible = false; // 控制卡片是否可见 11 | 12 | // 拖动相关状态 13 | let isDragging = false; 14 | let startX = 0; 15 | let startY = 0; 16 | let currentX = 0; 17 | let currentY = 0; 18 | 19 | // 初始化硬件加速 20 | card.style.transform = 'translate3d(0,0,0) perspective(1000px)'; 21 | 22 | // 处理拖动开始 23 | let dragStartTime = 0; 24 | let hasMoved = false; 25 | 26 | // 修改为监听卡片而不是容器的mousedown事件 27 | card.addEventListener('mousedown', (e) => { 28 | if (e.button !== 0) return; // 只响应左键 29 | 30 | // 检查点击是否在卡片区域内 31 | const rect = card.getBoundingClientRect(); 32 | if (e.clientX < rect.left || e.clientX > rect.right || 33 | e.clientY < rect.top || e.clientY > rect.bottom) { 34 | return; 35 | } 36 | 37 | isDragging = true; 38 | hasMoved = false; 39 | dragStartTime = Date.now(); 40 | startX = e.clientX - currentX; 41 | startY = e.clientY - currentY; 42 | container.style.transition = 'none'; // 拖动时禁用过渡 43 | e.preventDefault(); // 防止选中文本 44 | }); 45 | 46 | // 处理拖动 47 | document.addEventListener('mousemove', (e) => { 48 | if (!isDragging) return; 49 | const deltaX = Math.abs(e.clientX - startX - currentX); 50 | const deltaY = Math.abs(e.clientY - startY - currentY); 51 | // 如果移动超过3像素,标记为拖动而不是点击 52 | if (deltaX > 3 || deltaY > 3) { 53 | hasMoved = true; 54 | } 55 | currentX = e.clientX - startX; 56 | currentY = e.clientY - startY; 57 | // 更新容器位置,保持transform中的其他属性 58 | const translateStr = `translate(calc(-50% + ${currentX}px), calc(-50% + ${currentY}px))`; 59 | container.style.transform = isVisible ? translateStr : `${translateStr} translateY(calc(100vh + 50%))`; 60 | }); 61 | 62 | // 处理拖动结束 63 | document.addEventListener('mouseup', () => { 64 | isDragging = false; 65 | container.style.transition = 'transform 0.8s cubic-bezier(0.23, 1, 0.32, 1)'; 66 | }); 67 | 68 | // 生成3D变换样式 69 | function updateTransform(rotateX, rotateY, scale = 1, z = 0) { 70 | return ` 71 | translate3d(0,0,0) 72 | perspective(1000px) 73 | rotateX(${rotateX}deg) 74 | rotateY(${rotateY}deg) 75 | translateZ(${z}px) 76 | scale3d(${scale}, ${scale}, ${scale}) 77 | `; 78 | } 79 | 80 | // 处理鼠标移动时的3D效果 81 | function handleMouseMove(e) { 82 | if (!isHovering) return; 83 | 84 | if (rafId) { 85 | cancelAnimationFrame(rafId); 86 | } 87 | 88 | const rect = card.getBoundingClientRect(); 89 | const centerX = rect.left + rect.width / 2; 90 | const centerY = rect.top + rect.height / 2; 91 | const deltaX = (e.clientX - centerX) * 0.05; 92 | const deltaY = (e.clientY - centerY) * 0.05; 93 | 94 | rafId = requestAnimationFrame(() => { 95 | // 更新卡片的3D变换 96 | card.style.transform = updateTransform(-deltaY, deltaX, 1.05, 50); 97 | 98 | // 更新正面的全息效果 99 | const percentX = ((e.clientX - rect.left) / rect.width * 100) + 40; 100 | const percentY = (e.clientY - rect.top) / rect.height * 100; 101 | frontOverlay.style.transform = ` 102 | translate(${percentX - 50}%, ${percentY - 50}%) 103 | rotate(-45deg) 104 | `; 105 | }); 106 | } 107 | 108 | // 节流函数:限制事件触发频率 109 | function throttle(func, limit = 16) { 110 | let inThrottle; 111 | return function(e) { 112 | if (!inThrottle) { 113 | func(e); 114 | inThrottle = true; 115 | setTimeout(() => inThrottle = false, limit); 116 | } 117 | } 118 | } 119 | 120 | // 事件监听器 121 | // 点击翻转卡片 122 | card.addEventListener('click', (e) => { 123 | // 如果是拖动操作,不触发翻转 124 | if (hasMoved) { 125 | e.stopPropagation(); 126 | return; 127 | } 128 | 129 | isFlipped = !isFlipped; 130 | cardInner.classList.toggle('is-flipped'); 131 | 132 | // 如果当前正在悬浮状态,更新正确面的hover效果 133 | if (isHovering) { 134 | // 移除两面的hover效果 135 | card.querySelector('.card-front').classList.remove('hover'); 136 | card.querySelector('.card-back').classList.remove('hover'); 137 | // 根据翻转状态添加正确面的hover效果 138 | if (!isFlipped) { 139 | card.querySelector('.card-front').classList.add('hover'); 140 | } else { 141 | card.querySelector('.card-back').classList.add('hover'); 142 | } 143 | } 144 | }); 145 | 146 | // 鼠标进入卡片 147 | card.addEventListener('mouseenter', (e) => { 148 | isHovering = true; 149 | card.style.transition = 'transform 0.2s ease-out'; 150 | // 根据当前面添加悬浮效果 151 | if (!isFlipped) { 152 | card.querySelector('.card-front').classList.add('hover'); 153 | } else { 154 | card.querySelector('.card-back').classList.add('hover'); 155 | } 156 | handleMouseMove(e); 157 | }); 158 | 159 | // 鼠标离开卡片 160 | card.addEventListener('mouseleave', () => { 161 | isHovering = false; 162 | if (rafId) { 163 | cancelAnimationFrame(rafId); 164 | } 165 | // 重置所有效果 166 | card.style.transition = 'transform 0.4s ease-out'; 167 | card.style.transform = updateTransform(0, 0); 168 | card.querySelector('.card-front').classList.remove('hover'); 169 | card.querySelector('.card-back').classList.remove('hover'); 170 | // 调整重置位置,向右偏移-10%(40%-50%) 171 | frontOverlay.style.transform = 'translate(-10%, -20%) rotate(-45deg)'; 172 | }); 173 | 174 | // 添加节流后的鼠标移动监听 175 | document.addEventListener('mousemove', throttle(handleMouseMove)); 176 | 177 | // 添加遮罩控制 178 | let revealCount = 0; 179 | const maxRevealCount = 5; 180 | const coveredImage = card.querySelector('.back-image.covered'); 181 | // 定义每次点击后的揭示进度(百分比) 182 | const revealProgress = [0.3, 0.4, 0.55, 0.7, 1.0]; 183 | 184 | function updateRevealMask() { 185 | // 使用预设的进度值 186 | const progress = revealProgress[revealCount - 1]; 187 | coveredImage.style.clipPath = `inset(0 0 0 ${progress * 100}%)`; // 从左到右逐渐隐藏遮罩图 188 | } 189 | 190 | // 监听方向键 191 | document.addEventListener('keydown', (e) => { 192 | // 上箭头键显示卡片 193 | if (e.code === 'ArrowUp' && !isVisible) { 194 | e.preventDefault(); 195 | isVisible = true; 196 | container.classList.add('visible'); 197 | // 重置拖动位置 198 | currentX = 0; 199 | currentY = 0; 200 | } 201 | 202 | // 右箭头键触发桌面和显示器入场动画,但必须先显示卡片 203 | if (e.code === 'ArrowRight' && isVisible && !document.querySelector('.desk').classList.contains('slide-in')) { 204 | e.preventDefault(); 205 | const desk = document.querySelector('.desk'); 206 | const monitor = document.querySelector('.monitor-frame'); 207 | 208 | // 先显示桌面 209 | desk.classList.add('slide-in'); 210 | 211 | // 延迟显示显示器 212 | setTimeout(() => { 213 | monitor.classList.add('slide-in'); 214 | }, 250); // 等待桌面动画完成 215 | } 216 | 217 | // 空格键揭示逻辑 218 | if (e.code === 'Space' && isFlipped && revealCount < maxRevealCount) { 219 | e.preventDefault(); 220 | if (revealCount === 0) { 221 | revealCount = 1; 222 | } else { 223 | revealCount++; 224 | } 225 | updateRevealMask(); 226 | } 227 | 228 | // 回车键自动填充表单 229 | if (e.code === 'Enter' && loginForm.classList.contains('visible')) { 230 | e.preventDefault(); 231 | const cardNumberInput = document.getElementById('cardNumber'); 232 | const passwordInput = document.getElementById('password'); 233 | 234 | // 创建打字机效果 235 | let cardNumber = "C3L38R0N5-C3NT-M1LL3-AB0NN335"; 236 | let password = "M3RC18CPLAR35ULTAT35TC1-D35U5"; 237 | let cardIndex = 0; 238 | let passwordIndex = 0; 239 | 240 | // 清空输入框 241 | cardNumberInput.value = ''; 242 | passwordInput.value = ''; 243 | 244 | // 为卡号创建打字机效果 245 | const typeCardNumber = setInterval(() => { 246 | if (cardIndex < cardNumber.length) { 247 | cardNumberInput.value += cardNumber[cardIndex]; 248 | cardIndex++; 249 | // 自动滚动到最右边 250 | cardNumberInput.scrollLeft = cardNumberInput.scrollWidth; 251 | } else { 252 | clearInterval(typeCardNumber); 253 | // 开始输入密码 254 | const typePassword = setInterval(() => { 255 | if (passwordIndex < password.length) { 256 | passwordInput.value += password[passwordIndex]; 257 | passwordIndex++; 258 | // 自动滚动到最右边 259 | passwordInput.scrollLeft = passwordInput.scrollWidth; 260 | } else { 261 | clearInterval(typePassword); 262 | } 263 | }, 30); // 每个字符之间的延迟 264 | } 265 | }, 30); // 每个字符之间的延迟 266 | } 267 | }); 268 | 269 | // 充值按钮交互 270 | const menuButtons = document.querySelector('.menu-buttons'); 271 | const loginForm = document.querySelector('.login-form'); 272 | const logo = document.querySelector('.screen-logo'); 273 | const gameCurrency = document.querySelector('.game-currency'); 274 | let hasCardBeenUsed = false; // 添加标记,记录卡是否已被使用 275 | 276 | document.querySelectorAll('.menu-btn').forEach((btn, index) => { 277 | btn.addEventListener('click', (e) => { 278 | if (btn.textContent === '充值') { 279 | // 清空表单 280 | document.getElementById('cardNumber').value = ''; 281 | document.getElementById('password').value = ''; 282 | 283 | menuButtons.classList.add('hidden'); 284 | logo.classList.add('hidden'); 285 | loginForm.classList.add('visible'); 286 | gameCurrency.classList.add('visible'); 287 | } else if (btn.textContent === '开始') { 288 | alert('不是,哥们,你还真想玩啊?'); 289 | } else if (btn.textContent === '退出') { 290 | const shutdownScreen = document.querySelector('.shutdown-screen'); 291 | shutdownScreen.classList.add('visible'); 292 | 293 | // Windows XP 蓝屏文本 294 | const shutdownText = [ 295 | "A problem has been detected and Windows has been shut down to prevent damage", 296 | "to your computer.", 297 | "", 298 | "DRIVER_IRQL_NOT_LESS_OR_EQUAL", 299 | "", 300 | "If this is the first time you've seen this stop error screen,", 301 | "restart your computer. If this screen appears again, follow", 302 | "these steps:", 303 | "", 304 | "Check to make sure any new hardware or software is properly installed.", 305 | "If this is a new installation, ask your hardware or software manufacturer", 306 | "for any Windows updates you might need.", 307 | "", 308 | "If problems continue, disable or remove any newly installed hardware", 309 | "or software. Disable BIOS memory options such as caching or shadowing.", 310 | "If you need to use Safe Mode to remove or disable components, restart", 311 | "your computer, press F8 to select Advanced Startup Options, and then", 312 | "select Safe Mode." 313 | ]; 314 | 315 | let currentLine = 0; 316 | shutdownScreen.textContent = ''; 317 | 318 | // 逐行显示文本 319 | const typeText = () => { 320 | if (currentLine < shutdownText.length) { 321 | shutdownScreen.textContent += shutdownText[currentLine] + '\n'; 322 | currentLine++; 323 | setTimeout(typeText, 50); // 每行之间的延迟从100ms改为50ms 324 | } 325 | }; 326 | 327 | typeText(); 328 | } 329 | }); 330 | }); 331 | 332 | // 处理取消按钮 333 | const cancelBtn = loginForm.querySelector('.cancel-btn'); 334 | cancelBtn.addEventListener('click', () => { 335 | loginForm.classList.remove('visible'); 336 | gameCurrency.classList.remove('visible'); 337 | logo.classList.remove('hidden'); 338 | menuButtons.classList.remove('hidden'); 339 | }); 340 | 341 | // 处理表单提交 342 | loginForm.addEventListener('submit', (e) => { 343 | e.preventDefault(); 344 | const cardNumber = document.getElementById('cardNumber').value; 345 | const password = document.getElementById('password').value; 346 | 347 | // 验证卡号和密码,并检查卡是否已使用 348 | if (cardNumber === "C3L38R0N5-C3NT-M1LL3-AB0NN335" && 349 | password === "M3RC18CPLAR35ULTAT35TC1-D35U5" && 350 | !hasCardBeenUsed) { 351 | // 标记卡片已使用 352 | hasCardBeenUsed = true; 353 | 354 | // 更新赛车币数量 355 | gameCurrency.textContent = "赛车币:200"; 356 | 357 | // 立即恢复界面 358 | loginForm.classList.remove('visible'); 359 | gameCurrency.classList.remove('visible'); 360 | // 重置表单状态 361 | loginForm.style.opacity = '0'; 362 | loginForm.style.pointerEvents = 'none'; 363 | loginForm.style.transform = 'translateY(20px)'; 364 | // 清空表单 365 | document.getElementById('cardNumber').value = ''; 366 | document.getElementById('password').value = ''; 367 | 368 | // 显示logo和菜单 369 | logo.classList.remove('hidden'); 370 | menuButtons.classList.remove('hidden'); 371 | 372 | // 显示成功提示,独立于界面动画 373 | const banner = document.querySelector('.notification-banner'); 374 | const text = document.querySelector('.notification-text'); 375 | text.textContent = '恭喜玩家"╰☆壹楗丶仨連ㄋ↖"成功充值200赛车币'; 376 | banner.classList.add('show'); 377 | 378 | // 3秒后隐藏提示 379 | setTimeout(() => { 380 | banner.classList.remove('show'); 381 | }, 3000); 382 | } else { 383 | // 显示错误提示 384 | const banner = document.querySelector('.notification-banner'); 385 | const text = document.querySelector('.notification-text'); 386 | text.textContent = '卡号或密码错误'; 387 | banner.classList.add('show'); 388 | 389 | // 2秒后隐藏错误提示 390 | setTimeout(() => { 391 | banner.classList.remove('show'); 392 | }, 2000); 393 | } 394 | }); 395 | }); -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | min-height: 100vh; 9 | background: rgb(235, 235, 235); 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | overflow: hidden; 14 | } 15 | 16 | .container { 17 | perspective: 2000px; 18 | transform-style: preserve-3d; 19 | padding: 50px; 20 | position: fixed; 21 | top: 50%; 22 | left: 50%; 23 | transform: translate(-50%, calc(100vh + 50%)); 24 | transition: transform 0.8s cubic-bezier(0.23, 1, 0.32, 1); 25 | z-index: 3; 26 | pointer-events: none; 27 | } 28 | 29 | .container.visible { 30 | transform: translate(-50%, -50%); 31 | pointer-events: auto; 32 | } 33 | 34 | .card { 35 | width: 380px; 36 | height: 240px; 37 | position: relative; 38 | transform-style: preserve-3d; 39 | cursor: pointer; 40 | transform: perspective(1000px) rotateX(0deg) rotateY(0deg) translateZ(0) scale3d(1, 1, 1); 41 | } 42 | 43 | .card-inner { 44 | position: relative; 45 | width: 100%; 46 | height: 100%; 47 | transform-style: preserve-3d; 48 | transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); 49 | } 50 | 51 | .card-inner.is-flipped { 52 | transform: rotateY(180deg); 53 | } 54 | 55 | .card-front { 56 | position: absolute; 57 | width: 100%; 58 | height: 100%; 59 | backface-visibility: hidden; 60 | border-radius: 8px; 61 | overflow: hidden; 62 | box-shadow: 0 0 0 rgba(74, 58, 255, 0), 0 0 0 rgba(138, 111, 255, 0); 63 | transition: box-shadow 0.2s ease-out; 64 | transform: translateZ(1px); 65 | } 66 | 67 | .card-front img { 68 | width: 100%; 69 | height: 100%; 70 | object-fit: cover; 71 | transform: none; 72 | background-color: #000; 73 | } 74 | 75 | .holographic-overlay { 76 | position: absolute; 77 | top: -30%; 78 | left: -30%; 79 | right: -30%; 80 | bottom: -30%; 81 | background: linear-gradient( 82 | 125deg, 83 | rgba(255, 255, 255, 0) 0%, 84 | rgba(255, 255, 255, 0.05) 10%, 85 | rgba(255, 255, 255, 0.2) 20%, 86 | rgba(255, 255, 255, 0.05) 30%, 87 | rgba(255, 255, 255, 0) 40% 88 | ); 89 | transform: rotate(-45deg); 90 | pointer-events: none; 91 | mix-blend-mode: overlay; 92 | transition: transform 0.1s ease-out; 93 | } 94 | 95 | .laser-effect { 96 | position: absolute; 97 | top: 0; 98 | left: -100%; 99 | width: 40%; 100 | height: 200%; 101 | background: linear-gradient( 102 | 90deg, 103 | transparent, 104 | rgba(255, 255, 255, 0.3), 105 | transparent 106 | ); 107 | animation: laser 4s infinite; 108 | transform: skewX(-25deg); 109 | border-radius: 8px; 110 | } 111 | 112 | @keyframes laser { 113 | 0% { 114 | left: -100%; 115 | } 116 | 40% { 117 | left: 0%; 118 | } 119 | 60% { 120 | left: 100%; 121 | } 122 | 100% { 123 | left: 200%; 124 | } 125 | } 126 | 127 | /* 修改卡片的光晕效果 */ 128 | .card-front::before { 129 | content: ''; 130 | position: absolute; 131 | inset: -2px; 132 | background: linear-gradient(45deg, 133 | #4A3AFF, /* 深蓝紫色 */ 134 | #8A6FFF, /* 中蓝紫色 */ 135 | #4A3AFF /* 深蓝紫色 */ 136 | ); 137 | z-index: -1; 138 | border-radius: 12px; 139 | filter: blur(10px); 140 | opacity: 0; 141 | transition: opacity 0.2s ease-out; 142 | } 143 | 144 | /* 悬浮时显示光晕 */ 145 | .card-front.hover::before { 146 | opacity: 0.5; 147 | } 148 | 149 | /* 添加卡片悬浮效果 */ 150 | .container:hover .card { 151 | animation: none; 152 | } 153 | 154 | /* 调整全息效果的过渡 */ 155 | .holographic-overlay { 156 | transition: transform 0.1s ease-out; 157 | } 158 | 159 | /* 添加悬浮时的阴影效果 */ 160 | .card-front.hover { 161 | box-shadow: 162 | 0 0 30px rgba(74, 58, 255, 0.3), 163 | 0 0 60px rgba(138, 111, 255, 0.2); 164 | } 165 | 166 | /* 添加背面样式 */ 167 | .card-back { 168 | position: absolute; 169 | width: 100%; 170 | height: 100%; 171 | backface-visibility: hidden; 172 | border-radius: 8px; 173 | overflow: hidden; 174 | transform: rotateY(180deg) translateZ(1px); /* 将背面也往前推,避免与正面重叠 */ 175 | background: #000; /* 添加背景色确保不透明 */ 176 | clip-path: inset(0 0 0 0 round 8px); 177 | } 178 | 179 | .card-back img { 180 | width: 100%; 181 | height: 100%; 182 | object-fit: cover; 183 | transform: none; 184 | background-color: #000; 185 | } 186 | 187 | /* 添加内部容器来处理圆角裁切 */ 188 | .card-content { 189 | position: absolute; 190 | width: 100%; 191 | height: 100%; 192 | clip-path: inset(0 0 0 0 round 8px); 193 | } 194 | 195 | /* 背面图片样式 */ 196 | .back-image { 197 | position: absolute; 198 | top: 0; 199 | left: 0; 200 | width: 100%; 201 | height: 100%; 202 | object-fit: cover; 203 | } 204 | 205 | .back-image.open { 206 | z-index: 1; /* 保持在底层 */ 207 | } 208 | 209 | .back-image.covered { 210 | z-index: 2; /* 在上层 */ 211 | clip-path: inset(0 0 0 0); /* 初始完全显示 */ 212 | transition: clip-path 0.3s ease-out; /* 只保留clip-path过渡 */ 213 | } 214 | 215 | /* 移除之前的遮罩层样式,我们不再需要外的遮罩元素 */ 216 | .reveal-mask { 217 | display: none; 218 | } 219 | 220 | /* 背景样式 */ 221 | .background { 222 | position: absolute; 223 | top: 0; 224 | left: 0; 225 | width: 100%; 226 | height: 100%; 227 | overflow: hidden; 228 | z-index: 0; 229 | background: none; 230 | } 231 | 232 | /* 登录表单样式 */ 233 | .login-form { 234 | position: absolute; 235 | top: 172px; 236 | right: 63px; /* 与logo对齐 */ 237 | width: calc(100% / 3); /* 与logo相同宽度 */ 238 | background: rgba(0, 0, 0, 0.6); 239 | padding: 25px calc(100% / 3 * 0.1); /* 左右padding各设为logo宽度的10% */ 240 | border-radius: 15px; 241 | backdrop-filter: blur(10px); 242 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); 243 | z-index: 1; 244 | opacity: 0; 245 | pointer-events: none; 246 | transform: translateY(20px); 247 | transition: opacity 0.3s ease-out, transform 0.3s ease-out; 248 | } 249 | 250 | .login-form.visible { 251 | opacity: 1 !important; 252 | pointer-events: auto !important; 253 | transform: translateY(0) !important; 254 | } 255 | 256 | .form-group { 257 | width: 100%; /* 占满内容区域 */ 258 | margin-bottom: 20px; 259 | } 260 | 261 | .form-group:last-child { 262 | display: flex; 263 | gap: 15px; 264 | margin-bottom: 0; 265 | } 266 | 267 | .form-group input { 268 | width: 100%; 269 | padding: 12px 15px; 270 | height: 45px; 271 | border: none; 272 | border-radius: 10px; 273 | background: rgba(255, 255, 255, 0.1); 274 | color: #fff; 275 | font-size: 16px; 276 | font-weight: 500; 277 | transition: all 0.3s ease; 278 | /* 添加滚动相关样式 */ 279 | white-space: nowrap; 280 | overflow-x: scroll; 281 | overflow-y: hidden; 282 | -webkit-overflow-scrolling: touch; /* 在iOS上提供平滑滚动 */ 283 | scrollbar-width: none; /* Firefox */ 284 | -ms-overflow-style: none; /* IE and Edge */ 285 | } 286 | 287 | /* 隐藏 Webkit 滚动条 */ 288 | .form-group input::-webkit-scrollbar { 289 | display: none; 290 | } 291 | 292 | .form-group input::placeholder { 293 | color: rgba(255, 255, 255, 0.5); 294 | } 295 | 296 | .form-group input:focus { 297 | outline: none; 298 | background: rgba(255, 255, 255, 0.2); 299 | box-shadow: 0 0 10px rgba(74, 58, 255, 0.3); 300 | } 301 | 302 | /* 按钮基础样式 */ 303 | .submit-btn, 304 | .cancel-btn { 305 | flex: 1; 306 | padding: 15px; 307 | border: none; 308 | border-radius: 10px; 309 | font-size: 18px; 310 | font-weight: 500; 311 | cursor: pointer; 312 | transition: all 0.3s ease; 313 | margin-bottom: 0; /* 移除底部间距 */ 314 | } 315 | 316 | /* 确认按钮样式 */ 317 | .submit-btn { 318 | background: linear-gradient(45deg, #4A3AFF, #8A6FFF); 319 | color: #fff; 320 | } 321 | 322 | /* 取消按钮样式 */ 323 | .cancel-btn { 324 | background: rgba(255, 255, 255, 0.1); 325 | color: #fff; 326 | border: 1px solid rgba(255, 255, 255, 0.2); 327 | } 328 | 329 | /* 按钮悬浮效果 */ 330 | .submit-btn:hover, 331 | .cancel-btn:hover { 332 | transform: translateY(-2px); 333 | } 334 | 335 | .submit-btn:hover { 336 | box-shadow: 0 5px 15px rgba(74, 58, 255, 0.4); 337 | } 338 | 339 | .cancel-btn:hover { 340 | background: rgba(255, 255, 255, 0.15); 341 | border-color: rgba(255, 255, 255, 0.3); 342 | } 343 | 344 | /* 按钮点击效果 */ 345 | .submit-btn:active, 346 | .cancel-btn:active { 347 | transform: translateY(0); 348 | } 349 | 350 | /* 表单淡出动画 */ 351 | .login-form.fade-out { 352 | opacity: 0; 353 | pointer-events: none; 354 | } 355 | 356 | /* 显示器边框样式 */ 357 | .monitor-frame { 358 | padding: 2px; 359 | position: fixed; 360 | top: 50%; 361 | left: 50%; 362 | transform: translate(calc(100vw + 50%), -50%); 363 | width: 889px; 364 | height: 515px; 365 | background: #1a1a1a; 366 | border-radius: 12px; 367 | transition: transform 0.8s cubic-bezier(0.23, 1, 0.32, 1); 368 | z-index: 2; 369 | } 370 | 371 | /* 显示器入场动画 */ 372 | .monitor-frame.slide-in { 373 | transform: translate(-50%, -50%); 374 | } 375 | 376 | /* 在小屏幕上缩放 */ 377 | @media (max-width: 1000px) { 378 | .monitor-frame { 379 | width: 90vw; 380 | height: calc((90vw - 44px) * 9 / 16 + 44px); 381 | } 382 | } 383 | 384 | .monitor-bezel { 385 | position: relative; 386 | width: 100%; 387 | height: 100%; 388 | background: #2a2a2a; 389 | border-radius: 10px; 390 | padding: 20px; 391 | box-shadow: 392 | inset 0 0 0 1px #3a3a3a, 393 | 0 10px 30px rgba(0, 0, 0, 0.5); 394 | } 395 | 396 | .monitor-screen { 397 | position: relative; 398 | width: 100%; 399 | height: 100%; 400 | background: #000; 401 | border-radius: 5px; 402 | overflow: hidden; 403 | /* 确保内容区域保持16:9 */ 404 | aspect-ratio: 16 / 9; 405 | } 406 | 407 | /* 电源指示灯 */ 408 | .power-indicator { 409 | position: absolute; 410 | right: 20px; 411 | bottom: 4px; 412 | width: 24px; 413 | height: 10px; 414 | background: #1a1a1a; 415 | border-radius: 3px; 416 | padding: 2px; 417 | box-shadow: 418 | inset 0 0 2px rgba(0, 0, 0, 0.5), 419 | 0 1px 0 rgba(255, 255, 255, 0.1); 420 | } 421 | 422 | .power-light { 423 | width: 5px; 424 | height: 5px; 425 | margin: 1px; 426 | border-radius: 50%; 427 | background: linear-gradient(45deg, #4CAF50, #45a049); 428 | box-shadow: 429 | 0 0 5px #4CAF50, 430 | 0 0 10px rgba(76, 175, 80, 0.5); 431 | animation: blink 2s infinite; 432 | } 433 | 434 | @keyframes blink { 435 | 0%, 100% { opacity: 1; } 436 | 50% { opacity: 0.3; } 437 | } 438 | 439 | /* 背景视频样式 */ 440 | .background-video { 441 | position: absolute; 442 | top: 0; 443 | left: 0; 444 | width: 100%; 445 | height: 100%; 446 | object-fit: cover; 447 | z-index: 0; 448 | opacity: 0.8; /* 稍微调低不透明度,让背景不那么刺眼 */ 449 | } 450 | 451 | /* 显示器底座样式 */ 452 | .monitor-stand { 453 | position: absolute; 454 | bottom: -80px; 455 | left: 50%; 456 | transform: translateX(-50%); 457 | perspective: 1000px; 458 | z-index: -1; 459 | } 460 | 461 | .stand-neck { 462 | width: 48px; 463 | height: 64px; 464 | background: linear-gradient(90deg, #1a1a1a, #2a2a2a, #1a1a1a); 465 | margin: 0 auto; 466 | border-radius: 0; 467 | transform: perspective(1000px) rotateX(25deg); 468 | box-shadow: 469 | inset 1px 0 0 rgba(255, 255, 255, 0.1), 470 | inset -1px 0 0 rgba(255, 255, 255, 0.1); 471 | position: relative; 472 | top: -12px; 473 | } 474 | 475 | .stand-base { 476 | width: 160px; 477 | height: 32px; 478 | background: linear-gradient(90deg, #1a1a1a, #2a2a2a, #1a1a1a); 479 | border-radius: 8px 8px 4px 4px; 480 | position: relative; 481 | transform: translateY(-16px) perspective(1000px) rotateX(25deg); 482 | box-shadow: 483 | 0 4px 8px rgba(0, 0, 0, 0.3), 484 | inset 0 1px 0 rgba(255, 255, 255, 0.1); 485 | } 486 | 487 | .stand-base::after { 488 | content: ''; 489 | position: absolute; 490 | bottom: 0; 491 | left: 50%; 492 | transform: translateX(-50%); 493 | width: 144px; 494 | height: 4px; 495 | background: #1a1a1a; 496 | border-radius: 0 0 4px 4px; 497 | } 498 | 499 | /* 在小屏幕上调整底座大小 */ 500 | @media (max-width: 900px) { 501 | .monitor-stand { 502 | bottom: -80px; 503 | } 504 | 505 | .stand-neck { 506 | width: 50px; 507 | height: 70px; 508 | top: -12px; 509 | } 510 | 511 | .stand-base { 512 | width: 160px; 513 | height: 35px; 514 | } 515 | 516 | .stand-base::after { 517 | width: 140px; 518 | } 519 | } 520 | 521 | /* 木质桌面样式 */ 522 | .desk { 523 | position: fixed; 524 | top: 100vh; 525 | left: 0; 526 | width: 100%; 527 | height: calc(100vh - (50% + 515px/2 + 80px - 16px)); 528 | background-image: url('wood.jpg'); 529 | background-size: cover; 530 | background-position: top center; 531 | box-shadow: 532 | 0 -10px 30px rgba(0, 0, 0, 0.3), 533 | inset 0 2px 5px rgba(255, 255, 255, 0.1); 534 | z-index: 1; 535 | transition: top 0.8s cubic-bezier(0.23, 1, 0.32, 1); 536 | } 537 | 538 | /* 桌面入场动画 */ 539 | .desk.slide-in { 540 | top: calc(50% + 515px/2 + 80px - 16px); 541 | } 542 | 543 | /* 移除之前的伪元素木纹效果 */ 544 | .desk::before, 545 | .desk::after { 546 | display: none; 547 | } 548 | 549 | /* 屏幕右上角logo样式 */ 550 | .screen-logo { 551 | position: absolute; 552 | top: 20px; 553 | right: 63px; 554 | width: calc(100% / 3); 555 | height: 140px; /* 设定固定高度,便于计算中心点 */ 556 | z-index: 1; 557 | object-fit: contain; 558 | pointer-events: none; 559 | animation: breathe 3s ease-in-out infinite; 560 | opacity: 1; 561 | transition: opacity 0.3s ease-out; 562 | } 563 | 564 | @keyframes breathe { 565 | 0%, 100% { 566 | transform: scale(1); 567 | } 568 | 50% { 569 | transform: scale(1.05); 570 | } 571 | } 572 | 573 | /* 菜单按钮容器 */ 574 | .menu-buttons { 575 | position: absolute; 576 | top: 180px; 577 | right: calc(63px + (100% / 3) * 0.1); 578 | display: flex; 579 | flex-direction: column; 580 | gap: 25px; 581 | z-index: 1; 582 | width: calc((100% / 3) * 0.8); 583 | opacity: 1; 584 | transform: translateY(0); 585 | transition: opacity 0.3s ease-out, transform 0.3s ease-out, pointer-events 0.3s ease-out; 586 | } 587 | 588 | /* 菜单按钮隐藏状态 */ 589 | .menu-buttons.hidden { 590 | opacity: 0; 591 | transform: translateY(-20px); 592 | pointer-events: none; 593 | } 594 | 595 | /* 菜单按钮样式 */ 596 | .menu-btn { 597 | width: 100%; 598 | padding: 15px; 599 | border: none; 600 | border-radius: 10px; 601 | background: linear-gradient(45deg, #4A3AFF, #8A6FFF); 602 | color: #fff; 603 | font-size: 18px; 604 | font-weight: 500; 605 | cursor: pointer; 606 | transition: all 0.3s ease; 607 | position: relative; 608 | overflow: hidden; 609 | } 610 | 611 | /* 按钮悬浮效果 */ 612 | .menu-btn:hover { 613 | transform: translateY(-2px) scale(1.05); 614 | box-shadow: 615 | 0 5px 15px rgba(74, 58, 255, 0.4), 616 | 0 0 20px rgba(138, 111, 255, 0.3); 617 | } 618 | 619 | /* 按钮点击效果 */ 620 | .menu-btn:active { 621 | transform: translateY(0) scale(0.98); 622 | } 623 | 624 | /* 按钮光晕效果 */ 625 | .menu-btn::before { 626 | content: ''; 627 | position: absolute; 628 | top: -50%; 629 | left: -50%; 630 | width: 200%; 631 | height: 200%; 632 | background: radial-gradient( 633 | circle, 634 | rgba(255, 255, 255, 0.1) 0%, 635 | transparent 70% 636 | ); 637 | transform: scale(0); 638 | transition: transform 0.5s ease-out; 639 | } 640 | 641 | .menu-btn:hover::before { 642 | transform: scale(1); 643 | } 644 | 645 | /* 游戏币文本样式 */ 646 | .game-currency { 647 | position: absolute; 648 | /* 计算垂直位置: 649 | logo的top(20px) + (logo高度 - 文本高度)/2 650 | logo高度 = 140px 651 | 文本高度 = 54px (padding 15px * 2 + 文字大小 24px) 652 | = 20 + (140 - 54)/2 653 | = 63px 654 | */ 655 | top: 63px; 656 | right: 63px; 657 | width: calc(100% / 3); 658 | padding: 15px; 659 | color: #fff; 660 | font-size: 24px; 661 | font-weight: bold; 662 | text-align: center; 663 | background: rgba(0, 0, 0, 0.6); 664 | border-radius: 10px; 665 | backdrop-filter: blur(10px); 666 | z-index: 1; 667 | opacity: 0; 668 | visibility: hidden; 669 | transition: opacity 0.3s ease-out, visibility 0.3s ease-out; 670 | } 671 | 672 | /* 当登录表单显示时的样式 */ 673 | .game-currency.visible { 674 | opacity: 1; 675 | visibility: visible; 676 | } 677 | 678 | .screen-logo.hidden { 679 | opacity: 0; 680 | visibility: hidden; 681 | } 682 | 683 | /* 通知横幅样式 */ 684 | .notification-banner { 685 | position: absolute; 686 | top: -50px; /* 初始位置在屏幕外 */ 687 | left: 0; 688 | width: 100%; 689 | padding: 12px; 690 | background: rgba(0, 0, 0, 0.8); 691 | color: #fff; 692 | font-size: 20px; 693 | text-align: center; 694 | transform: translateY(-100%); 695 | opacity: 0; 696 | z-index: 10; 697 | transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); 698 | backdrop-filter: blur(5px); 699 | border-bottom: 1px solid rgba(255, 255, 255, 0.1); 700 | } 701 | 702 | .notification-banner.show { 703 | transform: translateY(0); 704 | opacity: 1; 705 | top: 0; 706 | } 707 | 708 | .notification-text { 709 | display: inline-block; 710 | background: linear-gradient(45deg, #4A3AFF, #8A6FFF); 711 | -webkit-background-clip: text; 712 | background-clip: text; 713 | color: transparent; 714 | font-weight: bold; 715 | text-shadow: 0 0 10px rgba(74, 58, 255, 0.3); 716 | } 717 | 718 | /* 关机界面样式 */ 719 | .shutdown-screen { 720 | position: absolute; 721 | top: 0; 722 | left: 0; 723 | width: 100%; 724 | height: 100%; 725 | background: #0000aa; 726 | color: #ffffff; 727 | font-family: 'Lucida Console', Monaco, monospace; 728 | font-size: 14px; 729 | line-height: 1.5; 730 | padding: 40px; 731 | white-space: pre; 732 | display: none; 733 | z-index: 100; 734 | letter-spacing: 1px; 735 | } 736 | 737 | .shutdown-screen.visible { 738 | display: block; 739 | } 740 | 741 | /* 其他CSS保持不变... */ -------------------------------------------------------------------------------- /BV1YqZJYaEo5/milliniumMenu/wood.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErSanSan233/PersonalVideoScripts/624480cc4ab6ef6ac4a797d4877eb1bb715f5bd7/BV1YqZJYaEo5/milliniumMenu/wood.jpg --------------------------------------------------------------------------------