|
10 | 10 | from ..utils.iterables import adjacent_pairs
|
11 | 11 | from ..utils.simple_functions import fdiv
|
12 | 12 |
|
13 |
| -# from ..utils.simple_functions import clip |
| 13 | +from ..utils.simple_functions import clip |
14 | 14 | from ..utils.space_ops import angle_of_vector
|
15 | 15 | from ..utils.space_ops import angle_between_vectors
|
16 | 16 | from ..utils.space_ops import compass_directions
|
@@ -90,7 +90,7 @@ def get_unpositioned_tip(self, **kwargs):
|
90 | 90 | config = dict()
|
91 | 91 | config.update(self.tip_config)
|
92 | 92 | config.update(kwargs)
|
93 |
| - return ArrowTip(**config) |
| 93 | + return OpenGLArrowTip(**config) |
94 | 94 |
|
95 | 95 | def position_tip(self, tip, at_start=False):
|
96 | 96 | # Last two control points, defining both
|
@@ -591,164 +591,176 @@ def get_last_handle(self):
|
591 | 591 | return self.submobjects[-1].get_points()[-2]
|
592 | 592 |
|
593 | 593 |
|
594 |
| -# class TangentLine(Line): |
595 |
| -# CONFIG = {"length": 1, "d_alpha": 1e-6} |
596 |
| -# |
597 |
| -# def __init__(self, vmob, alpha, **kwargs): |
598 |
| -# digest_config(self, kwargs) |
599 |
| -# da = self.d_alpha |
600 |
| -# a1 = clip(alpha - da, 0, 1) |
601 |
| -# a2 = clip(alpha + da, 0, 1) |
602 |
| -# super().__init__(vmob.pfp(a1), vmob.pfp(a2), **kwargs) |
603 |
| -# self.scale(self.length / self.get_length()) |
604 |
| -# |
605 |
| -# |
606 |
| -# class Elbow(VMobject): |
607 |
| -# CONFIG = { |
608 |
| -# "width": 0.2, |
609 |
| -# "angle": 0, |
610 |
| -# } |
611 |
| -# |
612 |
| -# def __init__(self, **kwargs): |
613 |
| -# super().__init__(self, **kwargs) |
614 |
| -# self.set_points_as_corners([UP, UP + RIGHT, RIGHT]) |
615 |
| -# self.set_width(self.width, about_point=ORIGIN) |
616 |
| -# self.rotate(self.angle, about_point=ORIGIN) |
617 |
| -# |
618 |
| -# |
619 |
| -# class Arrow(Line): |
620 |
| -# CONFIG = { |
621 |
| -# "fill_color": GREY_A, |
622 |
| -# "fill_opacity": 1, |
623 |
| -# "stroke_width": 0, |
624 |
| -# "buff": MED_SMALL_BUFF, |
625 |
| -# "thickness": 0.05, |
626 |
| -# "tip_width_ratio": 5, |
627 |
| -# "tip_angle": PI / 3, |
628 |
| -# "max_tip_length_to_length_ratio": 0.5, |
629 |
| -# "max_width_to_length_ratio": 0.1, |
630 |
| -# } |
631 |
| -# |
632 |
| -# def set_points_by_ends(self, start, end, buff=0, path_arc=0): |
633 |
| -# # Find the right tip length and thickness |
634 |
| -# vect = end - start |
635 |
| -# length = max(get_norm(vect), 1e-8) |
636 |
| -# thickness = self.thickness |
637 |
| -# w_ratio = fdiv(self.max_width_to_length_ratio, fdiv(thickness, length)) |
638 |
| -# if w_ratio < 1: |
639 |
| -# thickness *= w_ratio |
640 |
| -# |
641 |
| -# tip_width = self.tip_width_ratio * thickness |
642 |
| -# tip_length = tip_width / (2 * np.tan(self.tip_angle / 2)) |
643 |
| -# t_ratio = fdiv(self.max_tip_length_to_length_ratio, fdiv(tip_length, length)) |
644 |
| -# if t_ratio < 1: |
645 |
| -# tip_length *= t_ratio |
646 |
| -# tip_width *= t_ratio |
647 |
| -# |
648 |
| -# # Find points for the stem |
649 |
| -# if path_arc == 0: |
650 |
| -# points1 = (length - tip_length) * np.array([RIGHT, 0.5 * RIGHT, ORIGIN]) |
651 |
| -# points1 += thickness * UP / 2 |
652 |
| -# points2 = points1[::-1] + thickness * DOWN |
653 |
| -# else: |
654 |
| -# # Solve for radius so that the tip-to-tail length matches |end - start| |
655 |
| -# a = 2 * (1 - np.cos(path_arc)) |
656 |
| -# b = -2 * tip_length * np.sin(path_arc) |
657 |
| -# c = tip_length ** 2 - length ** 2 |
658 |
| -# R = (-b + np.sqrt(b ** 2 - 4 * a * c)) / (2 * a) |
659 |
| -# |
660 |
| -# # Find arc points |
661 |
| -# points1 = Arc.create_quadratic_bezier_points(path_arc) |
662 |
| -# points2 = np.array(points1[::-1]) |
663 |
| -# points1 *= R + thickness / 2 |
664 |
| -# points2 *= R - thickness / 2 |
665 |
| -# if path_arc < 0: |
666 |
| -# tip_length *= -1 |
667 |
| -# rot_T = rotation_matrix_transpose(PI / 2 - path_arc, OUT) |
668 |
| -# for points in points1, points2: |
669 |
| -# points[:] = np.dot(points, rot_T) |
670 |
| -# points += R * DOWN |
671 |
| -# |
672 |
| -# self.set_points(points1) |
673 |
| -# # Tip |
674 |
| -# self.add_line_to(tip_width * UP / 2) |
675 |
| -# self.add_line_to(tip_length * LEFT) |
676 |
| -# self.tip_index = len(self.get_points()) - 1 |
677 |
| -# self.add_line_to(tip_width * DOWN / 2) |
678 |
| -# self.add_line_to(points2[0]) |
679 |
| -# # Close it out |
680 |
| -# self.append_points(points2) |
681 |
| -# self.add_line_to(points1[0]) |
682 |
| -# |
683 |
| -# if length > 0: |
684 |
| -# # Final correction |
685 |
| -# super().scale(length / self.get_length()) |
686 |
| -# |
687 |
| -# self.rotate(angle_of_vector(vect) - self.get_angle()) |
688 |
| -# self.rotate( |
689 |
| -# PI / 2 - np.arccos(normalize(vect)[2]), |
690 |
| -# axis=rotate_vector(self.get_unit_vector(), -PI / 2), |
691 |
| -# ) |
692 |
| -# self.shift(start - self.get_start()) |
693 |
| -# self.refresh_triangulation() |
694 |
| -# |
695 |
| -# def reset_points_around_ends(self): |
696 |
| -# self.set_points_by_ends( |
697 |
| -# self.get_start(), self.get_end(), path_arc=self.path_arc |
698 |
| -# ) |
699 |
| -# return self |
700 |
| -# |
701 |
| -# def get_start(self): |
702 |
| -# nppc = self.n_points_per_curve |
703 |
| -# points = self.get_points() |
704 |
| -# return (points[0] + points[-nppc]) / 2 |
705 |
| -# |
706 |
| -# def get_end(self): |
707 |
| -# return self.get_points()[self.tip_index] |
708 |
| -# |
709 |
| -# def put_start_and_end_on(self, start, end): |
710 |
| -# self.set_points_by_ends(start, end, buff=0, path_arc=self.path_arc) |
711 |
| -# return self |
712 |
| -# |
713 |
| -# def scale(self, *args, **kwargs): |
714 |
| -# super().scale(*args, **kwargs) |
715 |
| -# self.reset_points_around_ends() |
716 |
| -# return self |
717 |
| -# |
718 |
| -# def set_thickness(self, thickness): |
719 |
| -# self.thickness = thickness |
720 |
| -# self.reset_points_around_ends() |
721 |
| -# return self |
722 |
| -# |
723 |
| -# def set_path_arc(self, path_arc): |
724 |
| -# self.path_arc = path_arc |
725 |
| -# self.reset_points_around_ends() |
726 |
| -# return self |
727 |
| -# |
728 |
| -# |
729 |
| -# class Vector(Arrow): |
730 |
| -# CONFIG = { |
731 |
| -# "buff": 0, |
732 |
| -# } |
733 |
| -# |
734 |
| -# def __init__(self, direction=RIGHT, **kwargs): |
735 |
| -# if len(direction) == 2: |
736 |
| -# direction = np.hstack([direction, 0]) |
737 |
| -# super().__init__(ORIGIN, direction, **kwargs) |
738 |
| -# |
739 |
| -# |
740 |
| -# class DoubleArrow(Arrow): |
741 |
| -# def __init__(self, *args, **kwargs): |
742 |
| -# Arrow.__init__(self, *args, **kwargs) |
743 |
| -# self.add_tip(at_start=True) |
744 |
| -# |
745 |
| -# |
746 |
| -# class CubicBezier(VMobject): |
747 |
| -# def __init__(self, a0, h0, h1, a1, **kwargs): |
748 |
| -# VMobject.__init__(self, **kwargs) |
749 |
| -# self.add_cubic_bezier_curve(a0, h0, h1, a1) |
750 |
| -# |
751 |
| -# |
| 594 | +class OpenGLTangentLine(OpenGLLine): |
| 595 | + def __init__(self, vmob, alpha, length=1, d_alpha=1e-6, **kwargs): |
| 596 | + self.length = length |
| 597 | + self.d_alpha = d_alpha |
| 598 | + da = self.d_alpha |
| 599 | + a1 = clip(alpha - da, 0, 1) |
| 600 | + a2 = clip(alpha + da, 0, 1) |
| 601 | + super().__init__(vmob.pfp(a1), vmob.pfp(a2), **kwargs) |
| 602 | + self.scale(self.length / self.get_length()) |
| 603 | + |
| 604 | + |
| 605 | +class OpenGLElbow(OpenGLVMobject): |
| 606 | + def __init__(self, width=0.2, angle=0, **kwargs): |
| 607 | + self.angle = angle |
| 608 | + super().__init__(self, **kwargs) |
| 609 | + self.set_points_as_corners([UP, UP + RIGHT, RIGHT]) |
| 610 | + self.set_width(width, about_point=ORIGIN) |
| 611 | + self.rotate(self.angle, about_point=ORIGIN) |
| 612 | + |
| 613 | + |
| 614 | +class OpenGLArrow(OpenGLLine): |
| 615 | + def __init__( |
| 616 | + self, |
| 617 | + start=LEFT, |
| 618 | + end=RIGHT, |
| 619 | + path_arc=0, |
| 620 | + fill_color=GREY_A, |
| 621 | + fill_opacity=1, |
| 622 | + stroke_width=0, |
| 623 | + buff=MED_SMALL_BUFF, |
| 624 | + thickness=0.05, |
| 625 | + tip_width_ratio=5, |
| 626 | + tip_angle=PI / 3, |
| 627 | + max_tip_length_to_length_ratio=0.5, |
| 628 | + max_width_to_length_ratio=0.1, |
| 629 | + **kwargs |
| 630 | + ): |
| 631 | + self.thickness = thickness |
| 632 | + self.tip_width_ratio = tip_width_ratio |
| 633 | + self.tip_angle = tip_angle |
| 634 | + self.max_tip_length_to_length_ratio = max_tip_length_to_length_ratio |
| 635 | + self.max_width_to_length_ratio = max_width_to_length_ratio |
| 636 | + super().__init__( |
| 637 | + start=start, |
| 638 | + end=end, |
| 639 | + buff=buff, |
| 640 | + path_arc=path_arc, |
| 641 | + fill_color=fill_color, |
| 642 | + fill_opacity=fill_opacity, |
| 643 | + stroke_width=stroke_width, |
| 644 | + **kwargs |
| 645 | + ) |
| 646 | + |
| 647 | + def set_points_by_ends(self, start, end, buff=0, path_arc=0): |
| 648 | + # Find the right tip length and thickness |
| 649 | + vect = end - start |
| 650 | + length = max(get_norm(vect), 1e-8) |
| 651 | + thickness = self.thickness |
| 652 | + w_ratio = fdiv(self.max_width_to_length_ratio, fdiv(thickness, length)) |
| 653 | + if w_ratio < 1: |
| 654 | + thickness *= w_ratio |
| 655 | + |
| 656 | + tip_width = self.tip_width_ratio * thickness |
| 657 | + tip_length = tip_width / (2 * np.tan(self.tip_angle / 2)) |
| 658 | + t_ratio = fdiv(self.max_tip_length_to_length_ratio, fdiv(tip_length, length)) |
| 659 | + if t_ratio < 1: |
| 660 | + tip_length *= t_ratio |
| 661 | + tip_width *= t_ratio |
| 662 | + |
| 663 | + # Find points for the stem |
| 664 | + if path_arc == 0: |
| 665 | + points1 = (length - tip_length) * np.array([RIGHT, 0.5 * RIGHT, ORIGIN]) |
| 666 | + points1 += thickness * UP / 2 |
| 667 | + points2 = points1[::-1] + thickness * DOWN |
| 668 | + else: |
| 669 | + # Solve for radius so that the tip-to-tail length matches |end - start| |
| 670 | + a = 2 * (1 - np.cos(path_arc)) |
| 671 | + b = -2 * tip_length * np.sin(path_arc) |
| 672 | + c = tip_length ** 2 - length ** 2 |
| 673 | + R = (-b + np.sqrt(b ** 2 - 4 * a * c)) / (2 * a) |
| 674 | + |
| 675 | + # Find arc points |
| 676 | + points1 = OpenGLArc.create_quadratic_bezier_points(path_arc) |
| 677 | + points2 = np.array(points1[::-1]) |
| 678 | + points1 *= R + thickness / 2 |
| 679 | + points2 *= R - thickness / 2 |
| 680 | + if path_arc < 0: |
| 681 | + tip_length *= -1 |
| 682 | + rot_T = rotation_matrix_transpose(PI / 2 - path_arc, OUT) |
| 683 | + for points in points1, points2: |
| 684 | + points[:] = np.dot(points, rot_T) |
| 685 | + points += R * DOWN |
| 686 | + |
| 687 | + self.set_points(points1) |
| 688 | + # Tip |
| 689 | + self.add_line_to(tip_width * UP / 2) |
| 690 | + self.add_line_to(tip_length * LEFT) |
| 691 | + self.tip_index = len(self.get_points()) - 1 |
| 692 | + self.add_line_to(tip_width * DOWN / 2) |
| 693 | + self.add_line_to(points2[0]) |
| 694 | + # Close it out |
| 695 | + self.append_points(points2) |
| 696 | + self.add_line_to(points1[0]) |
| 697 | + |
| 698 | + if length > 0: |
| 699 | + # Final correction |
| 700 | + super().scale(length / self.get_length()) |
| 701 | + |
| 702 | + self.rotate(angle_of_vector(vect) - self.get_angle()) |
| 703 | + self.rotate( |
| 704 | + PI / 2 - np.arccos(normalize(vect)[2]), |
| 705 | + axis=rotate_vector(self.get_unit_vector(), -PI / 2), |
| 706 | + ) |
| 707 | + self.shift(start - self.get_start()) |
| 708 | + self.refresh_triangulation() |
| 709 | + |
| 710 | + def reset_points_around_ends(self): |
| 711 | + self.set_points_by_ends( |
| 712 | + self.get_start(), self.get_end(), path_arc=self.path_arc |
| 713 | + ) |
| 714 | + return self |
| 715 | + |
| 716 | + def get_start(self): |
| 717 | + nppc = self.n_points_per_curve |
| 718 | + points = self.get_points() |
| 719 | + return (points[0] + points[-nppc]) / 2 |
| 720 | + |
| 721 | + def get_end(self): |
| 722 | + return self.get_points()[self.tip_index] |
| 723 | + |
| 724 | + def put_start_and_end_on(self, start, end): |
| 725 | + self.set_points_by_ends(start, end, buff=0, path_arc=self.path_arc) |
| 726 | + return self |
| 727 | + |
| 728 | + def scale(self, *args, **kwargs): |
| 729 | + super().scale(*args, **kwargs) |
| 730 | + self.reset_points_around_ends() |
| 731 | + return self |
| 732 | + |
| 733 | + def set_thickness(self, thickness): |
| 734 | + self.thickness = thickness |
| 735 | + self.reset_points_around_ends() |
| 736 | + return self |
| 737 | + |
| 738 | + def set_path_arc(self, path_arc): |
| 739 | + self.path_arc = path_arc |
| 740 | + self.reset_points_around_ends() |
| 741 | + return self |
| 742 | + |
| 743 | + |
| 744 | +class OpenGLVector(OpenGLArrow): |
| 745 | + def __init__(self, direction=RIGHT, buff=0, **kwargs): |
| 746 | + self.buff = buff |
| 747 | + if len(direction) == 2: |
| 748 | + direction = np.hstack([direction, 0]) |
| 749 | + super().__init__(ORIGIN, direction, buff=buff, **kwargs) |
| 750 | + |
| 751 | + |
| 752 | +class OpenGLDoubleArrow(OpenGLArrow): |
| 753 | + def __init__(self, *args, **kwargs): |
| 754 | + OpenGLArrow.__init__(self, *args, **kwargs) |
| 755 | + self.add_tip(at_start=True) |
| 756 | + |
| 757 | + |
| 758 | +class OpenGLCubicBezier(OpenGLVMobject): |
| 759 | + def __init__(self, a0, h0, h1, a1, **kwargs): |
| 760 | + OpenGLVMobject.__init__(self, **kwargs) |
| 761 | + self.add_cubic_bezier_curve(a0, h0, h1, a1) |
| 762 | + |
| 763 | + |
752 | 764 | class OpenGLPolygon(OpenGLVMobject):
|
753 | 765 | def __init__(self, *vertices, **kwargs):
|
754 | 766 | self.vertices = vertices
|
|
0 commit comments