@@ -159,6 +159,9 @@ def __init__(
159
159
self .shade_in_3d : bool = shade_in_3d
160
160
self .tolerance_for_point_equality : float = tolerance_for_point_equality
161
161
self .n_points_per_cubic_curve : int = n_points_per_cubic_curve
162
+ self ._bezier_t_values : npt .NDArray [float ] = np .linspace (
163
+ 0 , 1 , n_points_per_cubic_curve
164
+ )
162
165
self .cap_style : CapStyleType = cap_style
163
166
super ().__init__ (** kwargs )
164
167
self .submobjects : list [VMobject ]
@@ -753,7 +756,7 @@ def set_anchors_and_handles(
753
756
assert len (anchors1 ) == len (handles1 ) == len (handles2 ) == len (anchors2 )
754
757
nppcc = self .n_points_per_cubic_curve # 4
755
758
total_len = nppcc * len (anchors1 )
756
- self .points = np .zeros ((total_len , self .dim ))
759
+ self .points = np .empty ((total_len , self .dim ))
757
760
# the following will, from the four sets, dispatch them in points such that
758
761
# self.points = [
759
762
# anchors1[0], handles1[0], handles2[0], anchors1[0], anchors1[1],
@@ -765,23 +768,61 @@ def set_anchors_and_handles(
765
768
return self
766
769
767
770
def clear_points (self ) -> None :
771
+ # TODO: shouldn't this return self instead of None?
768
772
self .points = np .zeros ((0 , self .dim ))
769
773
770
774
def append_points (self , new_points : Point3D_Array ) -> Self :
775
+ """Append the given ``new_points`` to the end of
776
+ :attr:`VMobject.points`.
777
+
778
+ Parameters
779
+ ----------
780
+ new_points
781
+ An array of 3D points to append.
782
+
783
+ Returns
784
+ -------
785
+ :class:`VMobject`
786
+ The VMobject itself, after appending ``new_points``.
787
+ """
771
788
# TODO, check that number new points is a multiple of 4?
772
789
# or else that if len(self.points) % 4 == 1, then
773
790
# len(new_points) % 4 == 3?
774
- self .points = np .append (self .points , new_points , axis = 0 )
791
+ n = len (self .points )
792
+ points = np .empty ((n + len (new_points ), self .dim ))
793
+ points [:n ] = self .points
794
+ points [n :] = new_points
795
+ self .points = points
775
796
return self
776
797
777
798
def start_new_path (self , point : Point3D ) -> Self :
778
- if len (self .points ) % 4 != 0 :
799
+ """Append a ``point`` to the :attr:`VMobject.points`, which will be the
800
+ beginning of a new Bézier curve in the path given by the points. If
801
+ there's an unfinished curve at the end of :attr:`VMobject.points`,
802
+ complete it by appending the last Bézier curve's start anchor as many
803
+ times as needed.
804
+
805
+ Parameters
806
+ ----------
807
+ point
808
+ A 3D point to append to :attr:`VMobject.points`.
809
+
810
+ Returns
811
+ -------
812
+ :class:`VMobject`
813
+ The VMobject itself, after appending ``point`` and starting a new
814
+ curve.
815
+ """
816
+ n_points = len (self .points )
817
+ nppc = self .n_points_per_curve
818
+ if n_points % nppc != 0 :
779
819
# close the open path by appending the last
780
820
# start anchor sufficiently often
781
821
last_anchor = self .get_start_anchors ()[- 1 ]
782
- for _ in range (4 - (len (self .points ) % 4 )):
783
- self .append_points ([last_anchor ])
784
- self .append_points ([point ])
822
+ closure = [last_anchor ] * (nppc - (n_points % nppc ))
823
+ self .append_points (closure + [point ])
824
+ else :
825
+ self .append_points ([point ])
785
826
return self
786
827
787
828
def add_cubic_bezier_curve (
@@ -863,7 +904,7 @@ def add_line_to(self, point: Point3D) -> Self:
863
904
----------
864
905
865
906
point
866
- end of the straight line.
907
+ The end of the straight line.
867
908
868
909
Returns
869
910
-------
@@ -873,8 +914,8 @@ def add_line_to(self, point: Point3D) -> Self:
873
914
nppcc = self .n_points_per_cubic_curve
874
915
self .add_cubic_bezier_curve_to (
875
916
* (
876
- interpolate (self .get_last_point (), point , a )
877
- for a in np . linspace ( 0 , 1 , nppcc ) [1 :]
917
+ interpolate (self .get_last_point (), point , t )
918
+ for t in self . _bezier_t_values [1 :]
878
919
)
879
920
)
880
921
return self
@@ -939,15 +980,54 @@ def close_path(self) -> None:
939
980
self .add_line_to (self .get_subpaths ()[- 1 ][0 ])
940
981
941
982
def add_points_as_corners (self , points : Iterable [Point3D ]) -> Iterable [Point3D ]:
942
- for point in points :
943
- self .add_line_to (point )
983
+ """Append multiple straight lines at the end of
984
+ :attr:`VMobject.points`, which connect the given ``points`` in order
985
+ starting from the end of the current path. These ``points`` would be
986
+ therefore the corners of the new polyline appended to the path.
987
+
988
+ Parameters
989
+ ----------
990
+ points
991
+ An array of 3D points representing the corners of the polyline to
992
+ append to :attr:`VMobject.points`.
993
+
994
+ Returns
995
+ -------
996
+ :class:`VMobject`
997
+ The VMobject itself, after appending the straight lines to its
998
+ path.
999
+ """
1000
+ points = np .asarray (points ).reshape (- 1 , self .dim )
1001
+ if self .has_new_path_started ():
1002
+ # Pop the last point from self.points and
1003
+ # add it to start_corners
1004
+ start_corners = np .empty ((len (points ), self .dim ))
1005
+ start_corners [0 ] = self .points [- 1 ]
1006
+ start_corners [1 :] = points [:- 1 ]
1007
+ end_corners = points
1008
+ self .points = self .points [:- 1 ]
1009
+ else :
1010
+ start_corners = points [:- 1 ]
1011
+ end_corners = points [1 :]
1012
+
1013
+ nppcc = self .n_points_per_cubic_curve
1014
+ new_points = np .empty ((nppcc * start_corners .shape [0 ], self .dim ))
1015
+ new_points [::nppcc ] = start_corners
1016
+ new_points [nppcc - 1 :: nppcc ] = end_corners
1017
+ for i , t in enumerate (self ._bezier_t_values ):
1018
+ new_points [i ::nppcc ] = interpolate (start_corners , end_corners , t )
1019
+
1020
+ self .append_points (new_points )
1021
+ # TODO: shouldn't this method return self instead of points?
944
1022
return points
945
1023
946
1024
def set_points_as_corners (self , points : Point3D_Array ) -> Self :
947
- """Given an array of points, set them as corner of the vmobject.
1025
+ """Given an array of points, set them as corners of the
1026
+ :class:`VMobject`.
948
1027
949
- To achieve that, this algorithm sets handles aligned with the anchors such that the resultant bezier curve will be the segment
950
- between the two anchors.
1028
+ To achieve that, this algorithm sets handles aligned with the anchors
1029
+ such that the resultant Bézier curve will be the segment between the
1030
+ two anchors.
951
1031
952
1032
Parameters
953
1033
----------
@@ -957,7 +1037,7 @@ def set_points_as_corners(self, points: Point3D_Array) -> Self:
957
1037
Returns
958
1038
-------
959
1039
:class:`VMobject`
960
- ``self``
1040
+ The VMobject itself, after setting the new points as corners.
961
1041
962
1042
963
1043
Examples
@@ -985,7 +1065,7 @@ def construct(self):
985
1065
# This will set the handles aligned with the anchors.
986
1066
# Id est, a bezier curve will be the segment from the two anchors such that the handles belongs to this segment.
987
1067
self .set_anchors_and_handles (
988
- * (interpolate (points [:- 1 ], points [1 :], a ) for a in np . linspace ( 0 , 1 , nppcc ) )
1068
+ * (interpolate (points [:- 1 ], points [1 :], t ) for t in self . _bezier_t_values )
989
1069
)
990
1070
return self
991
1071
@@ -1036,17 +1116,15 @@ def make_jagged(self) -> Self:
1036
1116
1037
1117
def add_subpath (self , points : Point3D_Array ) -> Self :
1038
1118
assert len (points ) % 4 == 0
1039
- self .points : Point3D_Array = np . append ( self . points , points , axis = 0 )
1119
+ self .append_points ( points )
1040
1120
return self
1041
1121
1042
1122
def append_vectorized_mobject (self , vectorized_mobject : VMobject ) -> None :
1043
- new_points = list (vectorized_mobject .points )
1044
-
1045
1123
if self .has_new_path_started ():
1046
1124
# Remove last point, which is starting
1047
1125
# a new path
1048
1126
self .points = self .points [:- 1 ]
1049
- self .append_points (new_points )
1127
+ self .append_points (vectorized_mobject . points )
1050
1128
1051
1129
def apply_function (self , function : MappingFunction ) -> Self :
1052
1130
factor = self .pre_function_handle_to_anchor_scale_factor
0 commit comments