In [1]:
from flatsurf import *
from flatsurf.geometry.polyhedra import *
In [2]:
s = platonic_dodecahedron()[1]
The default way plotting looks:
In [3]:
s.plot()
Out[3]:
Labels in the center of each polygon indicate the label of the polygon. Edge labels above indicate which polygon the edge is glued to.
Plotting the surface is controlled by a GraphicalSurface object. You can get the surface as follows:
In [4]:
gs = s.graphical_surface()
The graphical surface controls where polygons are drawn. You can glue a polygon across an edge using gs.make_adjacent(label,edge)
. A difficulty is that you need to know which edge is which. You can enable zero_flags
to see the zero vertex of each polygon.
In [5]:
gs.will_plot_zero_flags = True
In [6]:
gs.plot()
Out[6]:
FlatSurf just uses some simple algorithm to layout polygons. Sometimes they overlap for example. I'm troubled by that the picture is asymmetric: Polygon 4 should have a pentagon 10 sticking to it. We can see by counting counterclockwise from the red flag in polygon 4 that the 10 appears on edge 3. We can verify that with:
In [7]:
s.opposite_edge(4,3)
Out[7]:
We can move polygon 10 so that it is adjacent to polygon 4 with the command:
In [8]:
gs.make_adjacent(4,3)
Lets check that it worked:
In [9]:
s.plot()
Out[9]:
Oops. Lets also move polygon 11.
In [10]:
gs.make_adjacent(7,4)
In [11]:
gs.plot()
Out[11]:
There are other options which can change what is displayed on an edge. The option can also be passed to s.graphical_surface()
.
In [12]:
gs.process_options(edge_labels="gluings and number")
In [13]:
gs.plot()
Out[13]:
In [14]:
gs.process_options(edge_labels="number")
In [15]:
gs.plot()
Out[15]:
The Euclidean Cone Surface s
works in a different coordinate system then the graphical surface gs
. So, when we moved the polygon above, we had no affect on s
. In fact, the polygons of s
are all the same:
In [16]:
s.polygon(0)
Out[16]:
In [17]:
s.polygon(1)
Out[17]:
So really s
is a disjoint union of twelve copies of a standard pentagon with some edge gluings.
Lets now look at "graphical coordinates" i.e., the coordinates in which gs
works.
In [18]:
show(gs.plot(), axes=True)
I can tell that the point (3,5)
is in the unfolding, but I can't tell if it is in polygon 3 or 7. The GraphicalSurface gs
is made out of GraphicalPolygons which we can use to deal with this sort of thing.
In [19]:
gs.graphical_polygon(3).contains((3,5))
Out[19]:
In [20]:
gs.graphical_polygon(7).contains((3,5))
Out[20]:
Great. Now we can get the position of the point on the surface!
In [21]:
gp = gs.graphical_polygon(7)
pt = gp.transform_back((3,5))
pt
Out[21]:
Here we plot polygon 6 in its geometric coordinates with pt
.
In [22]:
s.polygon(6).plot()+point2d([pt],zorder=100)
Out[22]:
Lets convert it to a surface point and plot it!
In [23]:
spt = s.surface_point(7,pt)
spt
Out[23]:
In [24]:
s.plot() + spt.plot(color="red", size=20)
Out[24]:
Now I want to plot an upward trajectory through this point. Again, I have to deal with the fact that the coordinates might not match. You can get access to the transformation (a similarity) from geometric coordinates to graphical coordinates:
In [25]:
transformation = gs.graphical_polygon(6).transformation()
transformation
Out[25]:
Really we want the inverse:
In [26]:
inverse_transformation = ~transformation
inverse_transformation
Out[26]:
We just want the derivative of this similarity to transform the vertical direction. The derivative is a $2 \times 2$ matrix.
In [27]:
show(inverse_transformation.derivative())
In [28]:
direction = inverse_transformation.derivative()*vector((0,1))
direction
Out[28]:
We can use the point and the direction to get a tangent vector, which we convert to a trajectory, flow and plot.
In [29]:
tangent_vector = s.tangent_vector(7, pt, direction)
tangent_vector
Out[29]:
In [30]:
traj = tangent_vector.straight_line_trajectory()
traj.flow(100)
traj.is_closed()
Out[30]:
In [31]:
s.plot()+spt.plot(color="red")+traj.plot()
Out[31]:
It is possible to have more than one graphical surface. Maybe you want to have one where things look better.
To get a new suface, you can call s.graphical_surface()
again but with a cached=False
parameter.
In [32]:
pretty_gs = s.graphical_surface(polygon_labels=False, edge_labels=False, cached=False)
In [33]:
pretty_gs.plot()
Out[33]:
Current polygon printing options:
In [34]:
pretty_gs.polygon_options
Out[34]:
In [35]:
del pretty_gs.polygon_options["color"]
pretty_gs.polygon_options["rgbcolor"]="#ffeeee"
In [36]:
pretty_gs.non_adjacent_edge_options["thickness"] = 0.5
pretty_gs.non_adjacent_edge_options["color"] = "lightblue"
pretty_gs.will_plot_adjacent_edges = False
In [37]:
pretty_gs.plot()
Out[37]:
To use a non-default graphical surface you need to pass the graphical surface as a parameter.
In [38]:
pretty_gs.plot()+spt.plot(pretty_gs, color="red")+traj.plot(pretty_gs)
Out[38]:
Lets make it prettier by drawing some stars on the faces!
Find all saddle connections of length at most $\sqrt{16}$:
In [39]:
saddle_connections = s.saddle_connections(16)
The edges have length two so we will keep anything that has a different length.
In [40]:
saddle_connections2 = []
for sc in saddle_connections:
h = sc.holonomy()
if h[0]**2 + h[1]**2 != 4:
saddle_connections2.append(sc)
len(saddle_connections2)
Out[40]:
In [41]:
plot = pretty_gs.plot()
for sc in saddle_connections2:
plot += sc.plot(pretty_gs, color="red")
plot
Out[41]:
Plot using the original graphical surface.
In [42]:
plot = s.plot()
for sc in saddle_connections2:
plot += sc.plot(color="red")
plot
Out[42]:
In [43]:
s = translation_surfaces.arnoux_yoccoz(4)
In [44]:
s.plot()
Out[44]:
Here is an example with the edge labels centered on the edge.
In [45]:
gs = s.graphical_surface(cached=False)
In [46]:
del gs.polygon_options["color"]
gs.polygon_options["rgbcolor"]="#eee"
gs.edge_label_options["position"]="edge"
gs.edge_label_options["t"]=0.5
gs.edge_label_options["push_off"]=0
gs.edge_label_options["color"]="green"
gs.adjacent_edge_options["thickness"]=0.5
gs.will_plot_non_adjacent_edges=False
In [47]:
gs.plot()
Out[47]:
In [48]:
gs = s.graphical_surface(cached=False)
In [49]:
del gs.polygon_options["color"]
gs.polygon_options["rgbcolor"]="#eef"
gs.edge_label_options["position"]="outside"
gs.edge_label_options["t"]=0.5
gs.edge_label_options["push_off"]=0.02
gs.edge_label_options["color"]="green"
gs.adjacent_edge_options["thickness"]=0.5
gs.non_adjacent_edge_options["thickness"]=0.25
In [50]:
gs.plot()
Out[50]:
In [51]:
s = translation_surfaces.infinite_staircase()
In [52]:
gs = s.graphical_surface(polygon_labels=False, edge_labels=False)
In [53]:
gs.plot()
Out[53]:
The methods plot_polygon
, plot_polygon_label
, plot_edge
, plot_edge_label
, plot_zero_flag
are fairly
simple. They just call methods in a GraphicalPolygon which was passed as a parameter. We can replace any of these functions to customize their behaviour.
In [54]:
# Define a replacement method.
def plot_polygon(self, label, graphical_polygon, upside_down):
if label%2==0:
return graphical_polygon.plot_polygon(color="lightgreen")
else:
return graphical_polygon.plot_polygon(color="yellow")
In [55]:
# Replace the method in gs.
from types import MethodType
gs.plot_polygon = MethodType(plot_polygon, gs)
In [56]:
gs.plot()
Out[56]: