README.md: mention also the tetraflexagon example
[flexagon-toolkit.git] / src / flexagon / tritetraflexagon.py
1 #!/usr/bin/env python
2 #
3 # A generic model for a tri-tetraflexagon
4 #
5 # Copyright (C) 2018  Antonio Ospite <ao2@ao2.it>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 from math import pi
21
22
23 class Tile(object):
24     def __init__(self, square, index):
25         self.square = square
26         self.index = index
27
28     def calc_offset_in_square(self, side):
29         xoffset = side / 2 * ((self.index % 2) * 2 - 1)
30         yoffset = side / 2 * ((self.index > 1) * 2 - 1)
31
32         return xoffset, yoffset
33
34     @staticmethod
35     def calc_plan_coordinates(side, i, j):
36         xoffset = side / 2 + j * side
37         yoffset = side / 2 + i * side
38
39         return xoffset, yoffset
40
41     @staticmethod
42     def calc_angle_in_plan(i, j):
43         """The angle of a tile in the tetraflexagon plan."""
44         return pi * (i > 1)
45
46     def __str__(self):
47         return "%d,%d" % (self.square.index, self.index)
48
49
50 class Square(object):
51     def __init__(self, index):
52         self.index = index
53         self.tiles = []
54         for i in range(4):
55             tile = Tile(self, i)
56             self.tiles.append(tile)
57
58     def __str__(self):
59         output = ""
60         for i in range(0, 4):
61             output += str(self.tiles[i])
62             output += "\t"
63
64         return output
65
66
67 class TriTetraflexagon(object):
68     def __init__(self):
69         self.squares = []
70         for i in range(0, 3):
71             square = Square(i)
72             self.squares.append(square)
73
74         # A plan is described by a mapping of the tiles in the squares,
75         # repositioned on a 2d grid.
76         #
77         # In the map below, the grid has two rows, each element of the grid is
78         # a pair (s, t), where 's' is the index of the square, and 't' is the
79         # index of the tile in that square.
80         plan_map = [
81             [(2, 0), (2, 1), (0, 1), None,   None],
82             [None,   None,   (0, 3), (1, 2), (1, 3)],
83             [None,   None,   (2, 3), (2, 2), (0, 2)],
84             [(0, 0), (1, 1), (1, 0), None,   None],
85         ]
86
87         # Preallocate a bi-dimensional array for an inverse mapping, this is
88         # useful to retrieve the position in the plan given a tile.
89         self.plan_map_inv = [[-1 for t in h.tiles] for h in self.squares]
90
91         self.plan = []
92         for i, plan_map_row in enumerate(plan_map):
93             plan_row = []
94             for j, mapping in enumerate(plan_map_row):
95                 if mapping:
96                     square_index, tile_index = mapping
97                     square = self.squares[square_index]
98                     tile = square.tiles[tile_index]
99                     self.plan_map_inv[square_index][tile_index] = (i, j)
100                 else:
101                     tile = None
102
103                 plan_row.append(tile)
104
105             self.plan.append(plan_row)
106
107     def get_tile_plan_position(self, tile):
108         return self.plan_map_inv[tile.square.index][tile.index]
109
110     def __str__(self):
111         output = ""
112
113         for row in self.plan:
114             for tile in row:
115                 output += "%s\t" % str(tile)
116             output += "\n"
117
118         return output
119
120
121 def test():
122     tritetraflexagon = TriTetraflexagon()
123     print(tritetraflexagon)
124
125
126 if __name__ == "__main__":
127     test()