@@ -807,9 +807,17 @@ class ArcPolygon(VMobject):
807807 """
808808 The ArcPolygon is what it says, a polygon, but made from arcs.
809809 More versatile than the standard Polygon.
810- Accepts both Arc and ArcBetweenPoints and is instantiated like this:
810+
811+ Parameters
812+ ----------
813+ *arcs : Arc or ArcBetweenPoints
814+
815+ Example
816+ -------
811817 ArcPolygon(arc0,arc1,arc2,arcN,**kwargs)
812- For proper appearance the arcs should be seamlessly connected:
818+
819+
820+ For proper appearance the arcs should seamlessly connect:
813821 [a,b][b,c][c,a]
814822 If they don't, the gaps will be filled in with straight lines.
815823
@@ -822,8 +830,8 @@ class ArcPolygon(VMobject):
822830 def __init__ (self , * arcs , ** kwargs ):
823831 if not all ([isinstance (m , Arc ) or
824832 isinstance (m , ArcBetweenPoints ) for m in arcs ]):
825- raise Exception ("All ArcPolygon submobjects must be of"
826- "type Arc/ArcBetweenPoints" )
833+ raise ValueError ("All ArcPolygon submobjects must be of"
834+ "type Arc/ArcBetweenPoints" )
827835 VMobject .__init__ (self , ** kwargs )
828836 # Adding the arcs like this makes arcpolygon double as a group
829837 self .add (* arcs )
@@ -836,9 +844,7 @@ def __init__(self, *arcs, **kwargs):
836844 len_ratio = line .get_length () / arc1 .get_arc_length ()
837845 if math .isnan (len_ratio ) or math .isinf (len_ratio ):
838846 continue
839- line .insert_n_curves (
840- int (arc1 .get_num_curves () * len_ratio )
841- )
847+ line .insert_n_curves (int (arc1 .get_num_curves () * len_ratio ))
842848 self .append_points (line .get_points ())
843849
844850
@@ -924,71 +930,116 @@ class Tiling(VMobject):
924930 rotated the correct way, as well as having a vertex setup that
925931 allows proper transformations.
926932
927- It's instantiated like this:
928- Tiling(tile_prototype, xOffset, yOffset, xRange, yRange, **kwargs)
929- The tile prototype can be any Mobject, a VGroup or a function.
930- The function format is function(x,y), returning a Mobject/VGroup.
933+ Parameters
934+ ----------
935+ tile_prototype : Mobject or function(x,y) that returns a Mobject
936+ x_offset : nested list of Mobject methods and values
937+ y_offset : nested list of Mobject methods and values
938+ x_range : range
939+ y_range : range
940+
941+
942+ The tile prototype can be any Mobject (also groups) or a function.
943+ The function format is function(x,y), returning a Mobject.
931944 Using groups or functions allows the tiling to contain multiple
932945 different tiles or to simplify the following offset functions.
933946
934947 Next are two nested lists that determine how the tiles are arranged.
935948 The functions are typically Mobject.shift and Mobject.rotate.
936949 Each list has to contain sublists with Function/Value pairs.
937- Example for a simple shift along the X-Axis:
950+ More on this in the Examples section.
951+
952+ Last are two ranges: If both ranges are range(-1,1,1),
953+ that would result in a square grid of 9 tiles.
954+
955+ A Tiling can be directly drawn like a VGroup.
956+ Tiling.tile_dictionary[x][y] can be used to access individual tiles,
957+ to color them for example.
958+
959+ Examples
960+ --------
961+ The nested lists determining arrangement need more explanation.
962+ Example for a shift along the X-Axis, 1 in the positive direction:
938963 [[Mobject.shift,[1,0,0]]]
964+
965+ The origin tile at [x0,y0] won't be moved, but the tile at [x1,y0]
966+ will be moved to [1,0,0]. Likewise the tile at [x4,y0] will be moved
967+ to [4,0,0]
939968
940- Every move within the tiling applies a full sublist.
969+ Every step within the tiling applies a full sublist.
941970 Example for a shift with simultaneous rotation:
942971 [[Mobject.shift,[1,0,0],Mobject.rotate,np.pi]]
972+
973+ This would move the tile at [x1,y0] to [1,0,0] and rotates it 180°.
943974
944975 When multiple sublists are passed, they are applied alternating.
945976 Example for alternating shifting and rotating:
946977 [[Mobject.shift,[1,0,0]],[Mobject.rotate,np.pi]]
947978
948- Last are two ranges: If both ranges are range(-1,1,1),
949- that would result in a grid of 9 tiles.
979+ This would move the tile at [x1,y0] to [1,0,0], but wouldn't rotate
980+ it yet. The tile at [x2,y0] would still be moved to [1,0,0] and also
981+ rotated by 180°. The tile at [x3,y0] would be moved to [2,0,0] and
982+ still rotated by 180°.
950983
951984 Full example:
952985 Tiling(Square(),
953986 [[Mobject.shift,[2.1,0,0]]],
954987 [[Mobject.shift,[0,2.1,0]]],
955988 range(-1,1),
956989 range(-1,1))
957-
958- A Tiling can be directly drawn like a VGroup.
959- Tiling.tile_dictionary[x][y] can be used to access individual tiles,
960- to color them for example.
961990 """
962- def __init__ (self , tile_prototype , xOffset , yOffset , xRange , yRange , ** kwargs ):
991+ def __init__ (self , tile_prototype , x_offset , y_offset , x_range , y_range , ** kwargs ):
963992 VMobject .__init__ (self , ** kwargs )
964- # First we add one more to the range,
965- # so that a -1,1 step 1 range also gives us 3 tiles,
966- # [-1,0,1] as opposed to 2 [-1,0]
967- self .xRange = range (xRange .start ,xRange .stop + xRange .step ,xRange .step )
968- self .yRange = range (yRange .start ,yRange .stop + yRange .step ,yRange .step )
969-
993+ # Add one more to the ranges, so that a range(-1,1,1)
994+ # also gives us 3 tiles, [-1,0,1] as opposed to 2 [-1,0]
995+ self .x_range = range (x_range .start ,x_range .stop + x_range .step ,x_range .step )
996+ self .y_range = range (y_range .start ,y_range .stop + y_range .step ,y_range .step )
997+ self .x_offset = x_offset
998+ self .y_offset = y_offset
999+
9701000 # We need the tiles array for a VGroup, which in turn we need
9711001 # to draw the tiling and adjust it.
9721002 # Trying to draw the tiling directly will not properly work.
1003+ self .tile_prototype = tile_prototype
9731004 self .tile_dictionary = {}
974- self .kwargs = kwargs
975- for x in self .xRange :
1005+ self .tile_init_loop ()
1006+
1007+ def tile_init_loop (self ):
1008+ """
1009+ Loops through the ranges, creates the tiles by copying the
1010+ prototype, adds them to self and sorts them into the dictionary.
1011+ Calls apply_transforms to apply passed methods.
1012+ """
1013+ for x in self .x_range :
9761014 self .tile_dictionary [x ]= {}
977- for y in self .yRange :
978- if callable (tile_prototype ):
979- tile = tile_prototype (x ,y ).deepcopy ()
1015+ for y in self .y_range :
1016+ if callable (self . tile_prototype ):
1017+ tile = self . tile_prototype (x ,y ).deepcopy ()
9801018 else :
981- tile = tile_prototype .deepcopy ()
982- self .transform_tile (x ,xOffset ,tile )
983- self .transform_tile (y ,yOffset ,tile )
1019+ tile = self .tile_prototype .deepcopy ()
1020+ self .apply_transforms (x ,y ,tile )
9841021 self .add (tile )
9851022 self .tile_dictionary [x ][y ]= tile
986- # TODO: Once the config overhaul is far enough:
987- # Implement a way to apply kwargs/some dict to all tiles
1023+ # TODO: Once the config overhaul is far enough:
1024+ # Implement a way to apply kwargs to all tiles.
1025+ # The reason for this is that if multiple tilings
1026+ # are instantiated from one prototype, having a different basic
1027+ # tile setup is rather complicated now (set_fill etc).
1028+
1029+ def apply_transforms (self ,x ,y ,tile ):
1030+ """
1031+ Calls transform_tile once per dimension to position tiles.
1032+ Written like this to allow easy extending by Honeycomb.
1033+ """
1034+ self .transform_tile (x ,self .x_offset ,tile )
1035+ self .transform_tile (y ,self .y_offset ,tile )
9881036
989- # This method computes and applies the offsets for the tiles.
990- # Also multiplies inputs, which requires arrays to be numpy arrays.
9911037 def transform_tile (self ,position ,offset ,tile ):
1038+ """
1039+ This method computes and applies the offsets for the tiles,
1040+ in the given dimension.
1041+ multiplies inputs, which requires arrays to be numpy arrays.
1042+ """
9921043 # The number of different offsets the current axis has
9931044 offsets_nr = len (offset )
9941045 for i in range (offsets_nr ):
@@ -1004,17 +1055,25 @@ def transform_tile(self,position,offset,tile):
10041055 else :
10051056 magnitude = len (range (i ,position ,offsets_nr ))
10061057 offset [i ][0 + j * 2 ](tile ,magnitude * np .array (offset [i ][1 + j * 2 ]))
1007-
1058+
10081059
10091060class Graph ():
10101061 """
10111062 This class is for visual representation of graphs for graph theory.
10121063 (Not graphs of functions. Same term but entirely different things.)
10131064
1014- It's instantiated with a dictionary that represents the graph,
1015- a configuration dictionary for vertex appearance, and one for edges.
1016- The configuration dictionaries are optional.
1017- Graph(graph_dictionary, vertex_config=vc, edge_config=ec)
1065+ It's instantiated with a dictionary that represents the graph, and
1066+ optionally which types of Mobject to use as vertices/edges and
1067+ dicts for their standard attributes.
1068+
1069+ Parameters
1070+ ----------
1071+ graph : dict
1072+ vertex_type : MobjectClass, optional (default: Circle)
1073+ vertex_config : dict, optional
1074+ edge_type : MobjectClass, optional (default: ArcBetweenPoints)
1075+ edge_config : dict, optional
1076+
10181077
10191078 The keys for the graph have to be of type int in ascending order,
10201079 with each number denoting a vertex.
@@ -1026,23 +1085,27 @@ class Graph():
10261085 directions, but it'll be drawn once, from lower to higher number.
10271086 For example if vertex 2 is connected to vertex 0, that's ignored.
10281087 The config dictionary is used to initialize a Circle as the vertex.
1029- (Or an Annulus if annulus=True is passed to this class.)
10301088 It will override values passed as vertex_config to the Graph.
1089+
1090+ Examples
1091+ --------
10311092 Full example:
10321093 g = {0: [[0,0,0], [1, 2], {"color": BLUE}],
10331094 1: [[1,0,0], [0, 2], {"color": GRAY}],
10341095 2: [[0,1,0], [0, 1], {"color": PINK}]}
10351096 Graph(g,vertex_config={"radius": 0.2,"fill_opacity": 1},
10361097 edge_config={"stroke_width": 5,"color": RED})
10371098
1038- An edge is instantiated as an ArcBetweenPoints, and optionally
1099+ An edge usually instantiated as an ArcBetweenPoints, and optionally
10391100 individual config dictionaries can also be passed to them.
10401101 Example:
10411102 g = {0: [[0,0,0], [[1,{"angle": 2}], [2,{"color": WHITE}]]...
1103+
10421104
1043- Use Graph.vertices/Graph.edges/Graph.annuli for drawing.
1105+ Use Graph.vertices/Graph.edges for drawing.
10441106 """
1045- def __init__ (self , graph , vertex_config = {}, edge_config = {}, ** kwargs ):
1107+ def __init__ (self , graph , vertex_type = Circle , vertex_config = {},
1108+ edge_type = ArcBetweenPoints , edge_config = {}, ** kwargs ):
10461109 if not all (isinstance (n ,int ) for n in graph .keys ()):
10471110 raise ValueError ("All keys for the Graph dictionary have to be of type int" )
10481111 if not all (all (isinstance (m ,int ) or isinstance (m ,list )
@@ -1051,26 +1114,13 @@ def __init__(self, graph, vertex_config={}, edge_config={}, **kwargs):
10511114 raise ValueError ("Invalid Edge definition in Graph class. Use int or "
10521115 "[int,dict]." )
10531116
1054- self .graph = graph
1055- self .vertex_config = vertex_config
1056- self .edge_config = edge_config
1057- if kwargs .get ('annulus' , False ):
1058- self .annulus = True
1059- else :
1060- self .annulus = False
1061- self .make_graph ()
1062-
1063- def make_graph (self ):
10641117 self .vertices = VGroup ()
10651118 self .edges = VGroup ()
1066- self .annuli = VGroup ()
1067- for vertex , attributes in self .graph .items ():
1068- if self .annulus :
1069- self .annuli .add (Annulus (** {** self .vertex_config ,
1070- ** attributes [2 ]}).shift (attributes [0 ]))
1071- else :
1072- self .vertices .add (Circle (** {** self .vertex_config ,
1073- ** attributes [2 ]}).shift (attributes [0 ]))
1119+
1120+ # Loops over all key/value pairs of the graph dict.
1121+ for vertex , attributes in graph .items ():
1122+ self .vertices .add (vertex_type (** {** vertex_config ,
1123+ ** attributes [2 ]}).shift (attributes [0 ]))
10741124 for edge_definition in attributes [1 ]:
10751125 if isinstance (edge_definition , int ):
10761126 vertex_number = edge_definition
@@ -1079,9 +1129,9 @@ def make_graph(self):
10791129 vertex_number = edge_definition [0 ]
10801130 edge_kwargs = edge_definition [1 ]
10811131 if vertex < vertex_number :
1082- edge = ArcBetweenPoints (attributes [0 ],
1083- self . graph [vertex_number ][0 ],
1084- ** {"angle" : 0 ,
1085- ** self . edge_config ,
1086- ** edge_kwargs })
1132+ edge = edge_type (attributes [0 ],
1133+ graph [vertex_number ][0 ],
1134+ ** {"angle" : 0 ,
1135+ ** edge_config ,
1136+ ** edge_kwargs })
10871137 self .edges .add (edge )
0 commit comments