Add support for tetraflexagons
[flexagon-toolkit.git] / src / flexagon / tritetraflexagon.py
diff --git a/src/flexagon/tritetraflexagon.py b/src/flexagon/tritetraflexagon.py
new file mode 100755 (executable)
index 0000000..8a70069
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# A generic model for a tri-tetraflexagon
+#
+# Copyright (C) 2018  Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from math import pi
+
+
+class Tile(object):
+    def __init__(self, square, index):
+        self.square = square
+        self.index = index
+
+    @staticmethod
+    def calc_plan_coordinates(side, i, j):
+        xoffset = side / 2 + j * side
+        yoffset = side / 2 + i * side
+
+        return xoffset, yoffset
+
+    @staticmethod
+    def calc_angle_in_plan(i, j):
+        """The angle of a tile in the tetraflexagon plan."""
+        return pi * (i > 1)
+
+    def __str__(self):
+        return "%d,%d" % (self.square.index, self.index)
+
+
+class Square(object):
+    def __init__(self, index):
+        self.index = index
+        self.tiles = []
+        for i in range(4):
+            tile = Tile(self, i)
+            self.tiles.append(tile)
+
+    def __str__(self):
+        output = ""
+        for i in range(0, 4):
+            output += str(self.tiles[i])
+            output += "\t"
+
+        return output
+
+
+class TriTetraflexagon(object):
+    def __init__(self):
+        self.squares = []
+        for i in range(0, 3):
+            square = Square(i)
+            self.squares.append(square)
+
+        # A plan is described by a mapping of the tiles in the squares,
+        # repositioned on a 2d grid.
+        #
+        # In the map below, the grid has two rows, each element of the grid is
+        # a pair (s, t), where 's' is the index of the square, and 't' is the
+        # index of the tile in that square.
+        plan_map = [
+            [(2, 0), (2, 1), (0, 1), None,   None],
+            [None,   None,   (0, 3), (1, 2), (1, 3)],
+            [None,   None,   (2, 3), (2, 2), (0, 2)],
+            [(0, 0), (1, 1), (1, 0), None,   None],
+        ]
+
+        # Preallocate a bi-dimensional array for an inverse mapping, this is
+        # useful to retrieve the position in the plan given a tile.
+        self.plan_map_inv = [[-1 for t in h.tiles] for h in self.squares]
+
+        self.plan = []
+        for i, plan_map_row in enumerate(plan_map):
+            plan_row = []
+            for j, mapping in enumerate(plan_map_row):
+                if mapping:
+                    square_index, tile_index = mapping
+                    square = self.squares[square_index]
+                    tile = square.tiles[tile_index]
+                    self.plan_map_inv[square_index][tile_index] = (i, j)
+                else:
+                    tile = None
+
+                plan_row.append(tile)
+
+            self.plan.append(plan_row)
+
+    def get_tile_plan_position(self, tile):
+        return self.plan_map_inv[tile.square.index][tile.index]
+
+    def __str__(self):
+        output = ""
+
+        for row in self.plan:
+            for tile in row:
+                output += "%s\t" % str(tile)
+            output += "\n"
+
+        return output
+
+
+def test():
+    tritetraflexagon = TriTetraflexagon()
+    print(tritetraflexagon)
+
+
+if __name__ == "__main__":
+    test()