Merge pull request #3 from llfkj/master

drawing hexagons! including filling them with colours.
This commit is contained in:
Dorian Pula 2016-07-16 13:24:38 -04:00 committed by GitHub
commit 0bdf4f126d
3 changed files with 615 additions and 102 deletions

View File

@ -33,19 +33,11 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false "collapsed": false
}, },
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting strategygame.kv\n"
]
}
],
"source": [ "source": [
"%%file strategygame.kv\n", "%%file strategygame.kv\n",
"\n", "\n",
@ -70,19 +62,11 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false "collapsed": false
}, },
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting main.py\n"
]
}
],
"source": [ "source": [
"%%file main.py\n", "%%file main.py\n",
"from kivy.app import App\n", "from kivy.app import App\n",
@ -112,7 +96,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false "collapsed": false
}, },
@ -452,19 +436,11 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": null,
"metadata": { "metadata": {
"collapsed": false "collapsed": false
}, },
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting main.py\n"
]
}
],
"source": [ "source": [
"%%file main.py\n", "%%file main.py\n",
"from kivy.app import App\n", "from kivy.app import App\n",
@ -565,6 +541,23 @@
" size_hint: 1, .33\n" " size_hint: 1, .33\n"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## Trying to draw the hexmap\n",
"\n",
"We tried adding the ability to style our widgets for to draw the Hexmap using lines like this\n",
"\n",
" BU: /\n",
" TD: \\\n",
" L: |\n",
" R: |\n",
"We get hexs now, but we need to figure out how to do the aspect ratio properly now...for a spiky top, we need each grid widget to have aspec ratio $$\\sqrt{3}\\cdot height = width$$"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -572,7 +565,560 @@
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [] "source": [
"%%file strategygame.kv\n",
"\n",
"#:include debug.kv\n",
"\n",
"<StrategyGame>:\n",
" id: _game\n",
" main_map: _main_map\n",
" map_rows: 10\n",
" map_cols: 10\n",
" BoxLayout:\n",
" orientation: 'horizontal'\n",
" GridLayout:\n",
" id: _main_map\n",
" game: _game\n",
" cols: root.map_cols\n",
" size_hint: .75, 1\n",
" BoxLayout:\n",
" orientation: 'vertical'\n",
" size_hint: .25, 1\n",
" DebugLabel:\n",
" text: 'status'\n",
" size_hint: 1, .66\n",
" DebugLabel:\n",
" text: 'mini-map'\n",
" size_hint: 1, .33\n",
"\n",
"<BU>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.y, self.right, self.top)\n",
" width: 2\n",
"<TD>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.top, self.right, self.y)\n",
" width: 2\n",
"<L>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.y, self.x, self.top)\n",
" width: 2\n",
"<R>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.right, self.y, self.right, self.top)\n",
" width: 2\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%file main.py\n",
"import collections\n",
"\n",
"from kivy.app import App\n",
"from kivy import properties\n",
"from kivy import graphics\n",
"from kivy.uix import label\n",
"from kivy.uix.floatlayout import FloatLayout\n",
"import math\n",
"\n",
"MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n",
"\n",
"\n",
"class StrategyGame(FloatLayout):\n",
" main_map = properties.ObjectProperty(None)\n",
" map_rows = properties.NumericProperty(0)\n",
" map_cols = properties.NumericProperty(0)\n",
"\n",
" def __init__(self, **kwargs):\n",
" super(StrategyGame, self).__init__(**kwargs)\n",
"\n",
" number_of_regions = self.map_rows * self.map_cols\n",
" for region in xrange(0, number_of_regions):\n",
" row = region / self.map_cols\n",
" col = region % self.map_cols\n",
" self.main_map.add_widget(self.pick_hex_cell(row=row, col=col))\n",
"\n",
"\n",
" def pick_hex_cell(self, row, col):\n",
" row_mod = row % 6\n",
" if col % 2 == 0:\n",
" if row_mod == 0:\n",
" return BU()\n",
" elif row_mod in (1, 2):\n",
" return L()\n",
" elif row_mod == 3:\n",
" return TD()\n",
" elif row_mod in (4, 5):\n",
" return R()\n",
" else:\n",
" if row_mod == 0:\n",
" return TD()\n",
" elif row_mod in (1, 2):\n",
" return R()\n",
" elif row_mod == 3:\n",
" return BU()\n",
" elif row_mod in (4, 5):\n",
" return L()\n",
"\n",
"\n",
"class HexMapCell(label.Label):\n",
" def __init__(self, row=0, col=0, **kwargs):\n",
" self.region_in_map = MapCoords(row, col)\n",
" super(HexMapCell, self).__init__(**kwargs)\n",
"\n",
"class BU(HexMapCell):\n",
" pass\n",
"class TD(HexMapCell):\n",
" pass\n",
"class L(HexMapCell):\n",
" pass\n",
"class R(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class StrategyGameApp(App):\n",
" def build(self):\n",
" return StrategyGame()\n",
"\n",
"if __name__ == '__main__':\n",
" StrategyGameApp().run()\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"Replaced the debug labels and coloured the status and minimap two different ways. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%%file strategygame.kv\n",
"#:import math math\n",
"\n",
"<StrategyGame>:\n",
" id: _game\n",
" main_map: _main_map\n",
" map_rows: 30\n",
" map_cols: 10\n",
" BoxLayout:\n",
" orientation: 'horizontal'\n",
" GridLayout:\n",
" id: _main_map\n",
" game: _game\n",
" cols: root.map_cols\n",
" size_hint: .75, 1\n",
" BoxLayout:\n",
" orientation: 'vertical'\n",
" size_hint: .25, 1\n",
" Label:\n",
" id: _stats\n",
" text: 'status'\n",
" size_hint: 1, .66\n",
" canvas.before:\n",
" Color:\n",
" rgba: .49, .49, .81, 1\n",
" Rectangle:\n",
" pos: _stats.pos\n",
" size: _stats.size\n",
" Button:\n",
" text: 'mini-map'\n",
" size_hint: 1, .33\n",
" background_color: .75, .71, .99, 1\n",
"\n",
"\n",
"<BU>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.y, self.right, self.top)\n",
" width: 2\n",
"<TD>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.top, self.right, self.y)\n",
" width: 2\n",
"<L>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.x, self.y, self.x, self.top)\n",
" width: 2\n",
"<R>:\n",
" canvas:\n",
" Color:\n",
" rgba: (1,1,1,1)\n",
" Line:\n",
" points: (self.right, self.y, self.right, self.top)\n",
" width: 2\n",
"\n",
"<HexMapCell>:\n",
" size_hint: 1, None\n",
" height: self.width / math.sqrt(3)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%%file main.py\n",
"import collections\n",
"import random\n",
"\n",
"from kivy import app, properties\n",
"from kivy.uix import button, label\n",
"from kivy.uix.floatlayout import FloatLayout\n",
"\n",
"MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n",
"\n",
"\n",
"class StrategyGame(FloatLayout):\n",
" main_map = properties.ObjectProperty(None)\n",
" map_rows = properties.NumericProperty(0)\n",
" map_cols = properties.NumericProperty(0)\n",
"\n",
" def __init__(self, **kwargs):\n",
" super(StrategyGame, self).__init__(**kwargs)\n",
"\n",
" number_of_regions = self.map_rows * self.map_cols\n",
" for region in xrange(0, number_of_regions):\n",
" row = region / self.map_cols\n",
" col = region % self.map_cols\n",
"\n",
" # Add hex cells to make up the map.\n",
" hex_cell = self.pick_hex_cell(row=row, col=col)\n",
" self.main_map.add_widget(hex_cell)\n",
"\n",
" # Add overlay conditionally.\n",
" if (row % 6 == 2 and col % 2 == 0) or (row % 6 == 5 and col % 2 == 1):\n",
" print('({}, {})'.format(row, col))\n",
" self.add_widget(HexMapControlCell(hex_bind=hex_cell))\n",
"\n",
" @staticmethod\n",
" def pick_hex_cell(row, col):\n",
" row_mod = row % 6\n",
" if col % 2 == 0:\n",
" if row_mod == 0:\n",
" return BU()\n",
" elif row_mod in (1, 2):\n",
" return L()\n",
" elif row_mod == 3:\n",
" return TD()\n",
" elif row_mod in (4, 5):\n",
" return R()\n",
" else:\n",
" if row_mod == 0:\n",
" return TD()\n",
" elif row_mod in (1, 2):\n",
" return R()\n",
" elif row_mod == 3:\n",
" return BU()\n",
" elif row_mod in (4, 5):\n",
" return L()\n",
"\n",
"\n",
"class HexMapCell(label.Label):\n",
" def __init__(self, row=0, col=0, **kwargs):\n",
" super(HexMapCell, self).__init__(**kwargs)\n",
" self.coords = MapCoords(row, col)\n",
"\n",
"\n",
"class BU(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class TD(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class L(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class R(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class HexMapControlCell(button.Button):\n",
" def __init__(self, hex_bind=None, **kwargs):\n",
" super(HexMapControlCell, self).__init__(**kwargs)\n",
" self.hex_bind = hex_bind\n",
" self.background_color = random.random(), random.random(), random.random(), 1\n",
" self.bind(pos=self.reposition_control_cell, size=self.resize_control_cell)\n",
" self.text = '({}, {})'.format(self.hex_bind.coords.row, self.hex_bind.coords.col)\n",
"\n",
" def reposition_control_cell(self, obj, value):\n",
" self.pos = self.hex_bind.pos\n",
"\n",
" def resize_control_cell(self, obj, value):\n",
" self.height = self.hex_bind.height * 2\n",
" self.width = self.hex_bind.width * 2\n",
"\n",
"\n",
"class StrategyGameApp(app.App):\n",
" def build(self):\n",
" return StrategyGame()\n",
"\n",
"if __name__ == '__main__':\n",
" StrategyGameApp().run()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Warning** if you run this your will get an error!!"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"We should probably have a viewer and a hexagon map underneath!!\n",
"\n",
"But not for now...let's make an overlay of boxes...and label them with an \"offset-r)\n",
"\n",
"Hmmmm....overlay...is...having...issues. \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%%file strategygame.kv\n",
"#:import math math\n",
"#:include debug.kv\n",
"\n",
"<StrategyGame>:\n",
" id: _game\n",
" main_map: _main_map\n",
" map_rows: 30\n",
" map_cols: 10\n",
" BoxLayout:\n",
" orientation: 'horizontal'\n",
" GridLayout:\n",
" id: _main_map\n",
" game: _game\n",
" cols: root.map_cols\n",
" size_hint: .75, 1\n",
" BoxLayout:\n",
" orientation: 'vertical'\n",
" size_hint: .25, 1\n",
" Label:\n",
" id: _stats\n",
" text: 'status'\n",
" size_hint: 1, .66\n",
" canvas.before:\n",
" Color:\n",
" rgba: .49, .49, .81, 1\n",
" Rectangle:\n",
" pos: _stats.pos\n",
" size: _stats.size\n",
" Button:\n",
" text: 'mini-map'\n",
" size_hint: 1, .33\n",
" background_color: .75, .71, .99, 1\n",
"\n",
"<Hex@Label>:\n",
" pos_hint: {'center_x':.5, 'center_y':.5}\n",
" canvas.after:\n",
" Color:\n",
" rgba: 1,1,1,1\n",
" Ellipse:\n",
" segments: 6\n",
" pos: self.pos\n",
" size: min(self.width, self.height), min(self.width, self.height)\n",
"\n",
"<HexMapCell>:\n",
" size_hint: 1, None\n",
" height: self.width / math.sqrt(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%file main.py\n",
"import collections\n",
"import random\n",
"import math\n",
"from kivy import app, properties\n",
"from kivy.uix import button, label\n",
"from kivy.uix.floatlayout import FloatLayout\n",
"from kivy.graphics import Color, Ellipse, Line\n",
"from kivy.logger import Logger\n",
"\n",
"MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])\n",
"\n",
"\n",
"class StrategyGame(FloatLayout):\n",
" main_map = properties.ObjectProperty(None)\n",
" map_rows = properties.NumericProperty(0)\n",
" map_cols = properties.NumericProperty(0)\n",
"\n",
" def __init__(self, **kwargs):\n",
" super(StrategyGame, self).__init__(**kwargs)\n",
"\n",
" number_of_regions = self.map_rows * self.map_cols\n",
" for region in xrange(0, number_of_regions):\n",
" row = region / self.map_cols\n",
" col = region % self.map_cols\n",
"\n",
" # Add hex cells to make up the map.\n",
" hex_cell = self.pick_hex_cell(row=row, col=col)\n",
" self.main_map.add_widget(hex_cell)\n",
"\n",
" # Add overlay conditionally.\n",
" if (row % 6 == 1 and col % 2 == 1) or (row % 6 == 4 and col % 2 == 0):\n",
" print('({}, {})'.format(row, col))\n",
" #radius = math.sqrt(hex_cell.width**2 + hex_cell.height**2)\n",
" radius = 2*hex_cell.height\n",
" with hex_cell.canvas.after:\n",
" Color(1,0,1,1)\n",
" hex_cell.ell = Line(circle=(hex_cell.x, hex_cell.y,radius, 0, 360, 6), width=2)\n",
" hex_cell.bind(pos=hex_cell.update_pos, size=hex_cell.update_pos)\n",
"\n",
"\n",
" @staticmethod\n",
" def pick_hex_cell(row, col):\n",
" row_mod = row % 6\n",
" if col % 2 == 0:\n",
" if row_mod == 0:\n",
" return BU()\n",
" elif row_mod in (1, 2):\n",
" return L()\n",
" elif row_mod == 3:\n",
" return TD()\n",
" elif row_mod in (4, 5):\n",
" return R()\n",
" else:\n",
" if row_mod == 0:\n",
" return TD()\n",
" elif row_mod in (1, 2):\n",
" return R()\n",
" elif row_mod == 3:\n",
" return BU()\n",
" elif row_mod in (4, 5):\n",
" return L()\n",
"\n",
"\n",
"\n",
"class HexMapCell(label.Label):\n",
" def __init__(self, row=0, col=0, **kwargs):\n",
" super(HexMapCell, self).__init__(**kwargs)\n",
" self.coords = MapCoords(row, col)\n",
"\n",
" def update_pos(self, instance, value):\n",
" Logger.info(\"StratGame: {}\".format(instance))\n",
" #radius = math.sqrt(self.width**2 + self.height**2)\n",
" radius = 2*self.height\n",
" self.ell.circle = (self.x, self.y, radius, 0, 360, 6)\n",
"\n",
"\n",
"\n",
"class BU(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class TD(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class L(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"class R(HexMapCell):\n",
" pass\n",
"\n",
"\n",
"\n",
"class HexMapControlCell(button.Button):\n",
" def __init__(self, hex_bind=None, **kwargs):\n",
" super(HexMapControlCell, self).__init__(**kwargs)\n",
" self.hex_bind = hex_bind\n",
" self.background_color = random.random(), random.random(), random.random(), 1\n",
" self.bind(pos=self.reposition_control_cell, size=self.resize_control_cell)\n",
" self.text = '({}, {})'.format(self.hex_bind.coords.row, self.hex_bind.coords.col)\n",
"\n",
" def reposition_control_cell(self, obj, value):\n",
" self.pos = self.hex_bind.pos\n",
"\n",
" def resize_control_cell(self, obj, value):\n",
" self.height = self.hex_bind.height * 2\n",
" self.width = self.hex_bind.width * 2\n",
"\n",
"\n",
"class StrategyGameApp(app.App):\n",
" def build(self):\n",
" return StrategyGame()\n",
"\n",
"if __name__ == '__main__':\n",
" StrategyGameApp().run()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What did we learn? \n",
"\n",
"* angle 0 appears to be straight up for a circle or ellipse\n",
"* The radius of the hexagon is $2*height$ for a pointed top hexagon, and for flat top, the radius of the hexagon is $2*width$\n",
"* Make sure you fix the aspect ratio of the rectangles you're basing things now\n",
"* segments are inscribed **inside** the circle\n",
"* tiling problems are hard!!! get a mathematician.\n",
"* there is a utility for hex codes for colours\n",
"* draw the hexagon using a Line with a circle to make an outline instead of an Ellipse\n",
"\n",
"Cleaned some stuff up and added colours to the hexagons. "
]
}, },
{ {
"cell_type": "code", "cell_type": "code",

68
main.py
View File

@ -1,9 +1,14 @@
import collections import collections
import random import random
import math
from kivy import app, properties from kivy import app, properties
from kivy.uix import button, label from kivy.uix import button, label
from kivy.uix.floatlayout import FloatLayout from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Color, Ellipse, Line
from kivy.logger import Logger
import kivy.utils
from kivy.vector import Vector
MapCoords = collections.namedtuple('MapCoords', ['row', 'col']) MapCoords = collections.namedtuple('MapCoords', ['row', 'col'])
@ -22,35 +27,23 @@ class StrategyGame(FloatLayout):
col = region % self.map_cols col = region % self.map_cols
# Add hex cells to make up the map. # Add hex cells to make up the map.
hex_cell = self.pick_hex_cell(row=row, col=col) hex_cell = HexMapCell()
self.main_map.add_widget(hex_cell) self.main_map.add_widget(hex_cell)
# Add overlay conditionally. # Add overlay conditionally.
if (row % 6 == 2 and col % 2 == 0) or (row % 6 == 5 and col % 2 == 1): if (row % 6 == 1 and col % 2 == 1) or (row % 6 == 4 and col % 2 == 0):
print('({}, {})'.format(row, col)) print('({}, {})'.format(row, col))
self.add_widget(HexMapControlCell(hex_bind=hex_cell)) #radius = math.sqrt(hex_cell.width**2 + hex_cell.height**2)
radius = 2*hex_cell.height
@staticmethod solid_x = hex_cell.x - hex_cell.height*2
def pick_hex_cell(row, col): solid_y = hex_cell.y - hex_cell.height*2
row_mod = row % 6 solid_size = (4*hex_cell.height, 4*hex_cell.height)
if col % 2 == 0: with hex_cell.canvas.after:
if row_mod == 0: Color(1,0,1,1)
return BU() hex_cell.ell = Line(circle=(hex_cell.x, hex_cell.y,radius, 0, 360, 6), width=2)
elif row_mod in (1, 2): Color(*kivy.utils.get_random_color(alpha = .5))
return L() hex_cell.solid = Ellipse(pos = (solid_x, solid_y), size = solid_size, segments = 6 )
elif row_mod == 3: hex_cell.bind(pos=hex_cell.update_pos, size=hex_cell.update_pos)
return TD()
elif row_mod in (4, 5):
return R()
else:
if row_mod == 0:
return TD()
elif row_mod in (1, 2):
return R()
elif row_mod == 3:
return BU()
elif row_mod in (4, 5):
return L()
class HexMapCell(label.Label): class HexMapCell(label.Label):
@ -58,22 +51,15 @@ class HexMapCell(label.Label):
super(HexMapCell, self).__init__(**kwargs) super(HexMapCell, self).__init__(**kwargs)
self.coords = MapCoords(row, col) self.coords = MapCoords(row, col)
def update_pos(self, instance, value):
class BU(HexMapCell): #radius = math.sqrt(self.width**2 + self.height**2)
pass radius = 2*self.height
solid_x = self.x - self.height*2
solid_y = self.y - self.height*2
class TD(HexMapCell): solid_size = (4*self.height, 4*self.height)
pass self.ell.circle = (self.x, self.y, radius, 0, 360, 6)
self.solid.pos = (solid_x, solid_y)
self.solid.size = solid_size
class L(HexMapCell):
pass
class R(HexMapCell):
pass
class HexMapControlCell(button.Button): class HexMapControlCell(button.Button):
def __init__(self, hex_bind=None, **kwargs): def __init__(self, hex_bind=None, **kwargs):

View File

@ -1,4 +1,5 @@
#:import math math #:import math math
##:include debug.kv
<StrategyGame>: <StrategyGame>:
id: _game id: _game
@ -30,36 +31,16 @@
size_hint: 1, .33 size_hint: 1, .33
background_color: .75, .71, .99, 1 background_color: .75, .71, .99, 1
<Hex@Label>:
<BU>: pos_hint: {'center_x':.5, 'center_y':.5}
canvas: canvas.after:
Color: Color:
rgba: (1,1,1,1) rgba: 1,1,1,1
Line: Ellipse:
points: (self.x, self.y, self.right, self.top) segments: 6
width: 2 pos: self.pos
<TD>: size: min(self.width, self.height), min(self.width, self.height)
canvas:
Color:
rgba: (1,1,1,1)
Line:
points: (self.x, self.top, self.right, self.y)
width: 2
<L>:
canvas:
Color:
rgba: (1,1,1,1)
Line:
points: (self.x, self.y, self.x, self.top)
width: 2
<R>:
canvas:
Color:
rgba: (1,1,1,1)
Line:
points: (self.right, self.y, self.right, self.top)
width: 2
<HexMapCell>: <HexMapCell>:
size_hint: 1, None size_hint: 1, None
height: self.width / math.sqrt(3) height: self.width / math.sqrt(3)