Skip to content

Commit

Permalink
implement rotating and flipping, add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mwinkens committed May 19, 2024
1 parent 4477022 commit 315b792
Show file tree
Hide file tree
Showing 20 changed files with 724 additions and 133 deletions.
Binary file added data/img/tile_not_found_empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[pytest]
qt_qapp_name = viewer-test
minversion = 6.0
addopts = -ra -q
testpaths = tests
Expand Down
10 changes: 7 additions & 3 deletions src/buttons/button_tile_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from src.images_helper import ImageHelper
from src.logic.tile_handler import TileHandler
from src.signals.signal_emitter import NeighborClickedEmitter
from src.signals.signal_emitter import ConfigurationClickedEmitter

if TYPE_CHECKING:
from src.widgets.widget_tile import Tile
Expand All @@ -19,7 +19,7 @@ class TileConnectionButton(QAbstractButton):

def __init__(self, button_id, parent=None):
super().__init__(parent)
self.signal_emitter = NeighborClickedEmitter(parent)
self.signal_emitter = ConfigurationClickedEmitter(parent)
self.button_id = button_id
self._state = 2 # Any
self._num_states = 3
Expand Down Expand Up @@ -86,7 +86,11 @@ def setTile(self, tile: Optional["Tile"], update_neighbors=True):
(self._tile.getID() == tile.getID() or self._tile.tile_data == tile.tile_data):
return

self._tile = tile
if tile is None:
self._tile = tile
else:
self._tile = tile.__copy__()

if update_neighbors:
self._update_neighborhood()
self.update()
Expand Down
34 changes: 33 additions & 1 deletion src/buttons/button_tile_connection_center.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,24 @@ def __init__(self, tile: "Tile", button_id, parent=None):
["Rotate", "Allow center tile to be rotated"],
["Empty", "Allow center tile to be placed on empty blocks"],
]
self.checkboxes = []
for i, (name, tool_tip) in enumerate(checkbox_contents):
widget = QCheckBox(name)
widget.setToolTip(tool_tip)
self.checkboxes.append(widget)
layout.addWidget(widget, i // 2, i % 2)

# these are maybe not required but nice to have
self.box_v_flip = self.checkboxes[0]
self.box_h_flip = self.checkboxes[1]
self.box_rot = self.checkboxes[2]
self.box_empty = self.checkboxes[3]

self.box_v_flip.stateChanged.connect(self.updateVFlip)
self.box_h_flip.stateChanged.connect(self.updateHFlip)
self.box_rot.stateChanged.connect(self.updateRot)
self.box_empty.stateChanged.connect(self.updateEmpty)

self.setLayout(layout)

# override
Expand All @@ -46,6 +59,25 @@ def _paintText(self, qp: QPainter):
pass

def setTile(self, tile: Optional["Tile"], update_neighbors=True):
# TODO set checkboxes
self._tile = tile
self.box_h_flip.setCheckState(tile.tile_data.mods.can_h_flip)
self.box_v_flip.setCheckState(tile.tile_data.mods.can_v_flip)
self.box_rot.setCheckState(tile.tile_data.mods.can_rot)
self.box_empty.setCheckState(tile.tile_data.status.empty)
self.update()

def updateVFlip(self, _):
value = self.box_v_flip.isChecked()
self.signal_emitter.modification_signal.emit(0, value)

def updateHFlip(self, _):
value = self.box_h_flip.isChecked()
self.signal_emitter.modification_signal.emit(1, value)

def updateRot(self, _):
value = self.box_rot.isChecked()
self.signal_emitter.modification_signal.emit(2, value)

def updateEmpty(self, _):
value = self.box_empty.isChecked()
self.signal_emitter.modification_signal.emit(3, value)
40 changes: 22 additions & 18 deletions src/dialogs/dialog_check_map.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import random
from typing import TYPE_CHECKING, List, Optional, Any
from typing import List, Optional, Tuple

import numpy as np
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QPixmap, QPainter
from PyQt5.QtWidgets import QDialog, QGridLayout, QDialogButtonBox, QLabel

from src.logic.tile_status import TileStatus
from src.globals import EIGHT_NEIGHBORS
from src.images_helper import ImageHelper
from src.logic.tile_connection import TileConnection
from src.logic.tile_data import TileData
from src.logic.tile_handler import TileHandler
from widgets.widget_base_tile import BaseTile
from src.logic.tile_handler import TileHandler, NeighborhoodEntry
from src.widgets.widget_base_tile import BaseTile


class CheckMapDialog(QDialog):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent)
super().__init__(parent, *args, **kwargs)

self.setWindowTitle("Check tile configurations")
self.layout = QGridLayout()
Expand All @@ -39,12 +38,17 @@ def __init__(self, parent, *args, **kwargs):
grid_y = map_y + 1
grid_x = map_x + 1

widget = BaseTile()
widget.setFixedSize(QSize(64, 64))
pm = CheckMapDialog._getMapTilePixmap(solid_map, map_x, map_y)
if pm:
widget.setPixmap(pm)
self.layout.addWidget(widget, grid_y, grid_x, 1, 1)
tile, status = CheckMapDialog._getMapTile(solid_map, map_x, map_y)
if not tile:
tile = QLabel() # Empty tile
if solid_map[map_y, map_x] == 0:
tile.setPixmap(ImageHelper.instance().NO_TILE_FOUND_EMPTY)
else:
tile.setPixmap(ImageHelper.instance().NO_TILE_FOUND)
else:
tile.tile_data.status = status
tile.setFixedSize(QSize(64, 64))
self.layout.addWidget(tile, grid_y, grid_x, 1, 1)

# add ok and cancel button
q_btn = QDialogButtonBox.Ok | QDialogButtonBox.Cancel
Expand Down Expand Up @@ -83,14 +87,14 @@ def _getMapNeighborhood(solid_map: np.ndarray, x: int, y: int) -> List[int]:
return neighbors

@staticmethod
def _getMapTilePixmap(solid_map: np.ndarray, x, y) -> Optional[QPixmap]:
def _getMapTile(solid_map: np.ndarray, x, y) -> Tuple[Optional[BaseTile], Optional[TileStatus]]:
if solid_map[y, x] == 0:
return None # TODO this isn't true
return None, None # TODO this isn't true
neighbors = CheckMapDialog._getMapNeighborhood(solid_map, x, y)
tc = TileConnection(neighbors)
tile_id_list = TileHandler.instance().findTiles(tc)
tile_id_list: List[NeighborhoodEntry] = TileHandler.instance().findTiles(tc)
if len(tile_id_list):
rand_tile_id = random.randint(0, len(tile_id_list) - 1) # I could make more sure that every tile is used
return TileHandler.instance().getPixmap(tile_id_list[rand_tile_id])
else:
return ImageHelper.instance().NO_TILE_FOUND
tile_id, status = tile_id_list[rand_tile_id]
return TileHandler.instance().getTile(tile_id), status
return None, None
34 changes: 25 additions & 9 deletions src/dialogs/dialog_tile_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
from src.logic.tile_connection import TileConnection
from src.logic.tile_data import TileData
from src.logic.tile_handler import TileHandler
from src.logic.tile_status import TileStatus
from src.logic.tile_modificators import TileMods

if TYPE_CHECKING:
from src.widgets.widget_tile import Tile


class TileSettingsDialog(QDialog):
# noinspection PyUnresolvedReferences

def __init__(self, tile: "Tile", parent=None):
super().__init__(parent)
self.setWindowTitle("Configure Tile")
Expand All @@ -36,10 +38,11 @@ def __init__(self, tile: "Tile", parent=None):
if i == 4:
widget = TileConnectionCenterButton(tile, i, self)
self.center = widget
widget.signal_emitter.modification_signal.connect(self.onModificationChange)
else:
widget = TileConnectionButton(len(self.buttons), self)
self.buttons.append(widget)
widget.signal_emitter.neighbor_signal.connect(self.onConnectionButtonClick)
widget.signal_emitter.neighbor_signal.connect(self.onConnectionButtonClick)
self.layout.addWidget(widget, i // 3 + 1, i % 3 + 1, 1, 1)

# self.center.setTile(tile)
Expand All @@ -56,14 +59,13 @@ def __init__(self, tile: "Tile", parent=None):

self.setModal(True)
# TODO calculate smarter values with the tile itself
self._tile_data = TileData(TileConnection([2] * 8), False, False, False, False)
self._tile_data = TileData(TileConnection([2] * 8), TileStatus(), TileMods(False, False, False))

def getTileData(self):
return self._tile_data
return self._tile_data.__copy__()

def setTileData(self, data: TileData):
self._tile_data = data
print(f"Dialog tile data:\n{self._tile_data.con}")
self._tile_data = data.__copy__()

# update button states
for i, button_state in enumerate(self._tile_data.con.getNeighbors()):
Expand All @@ -76,7 +78,6 @@ def setTileData(self, data: TileData):
def onConnectionButtonClick(self, button_id: int):
# goal: update tile after a button was clicked
# find TileConnections, then ask handler if any tiles exist
print(f"button ID {button_id} clicked")
self._updateTileData(button_id) # update tile data
self._updateTile(button_id) # update own tile

Expand All @@ -86,6 +87,22 @@ def onConnectionButtonClick(self, button_id: int):
if neighbor_buttons[i] is not None:
self._updateTile(i)

def onModificationChange(self, modification: int, value: bool):
match modification:
case 0:
self._tile_data.mods.can_v_flip = value
return
case 1:
self._tile_data.mods.can_h_flip = value
return
case 2:
self._tile_data.mods.can_rot = value
return
case 3:
self._tile_data.status.empty = value
return
raise ValueError(f"Modification {modification} unknown")

def _updateTileData(self, button_id: int):
state = self.buttons[button_id].checkStateSet()
self._tile_data.con.setNeighbor(button_id, state)
Expand All @@ -104,7 +121,7 @@ def _updateTile(self, button_id: int):
# yay, I found a tile that connects in this location
# use a random one, because this shouldn't matter
rand_tile = random.randint(0, len(tile_id_list) - 1)
tile_id = tile_id_list[rand_tile]
tile_id, _ = tile_id_list[rand_tile]
tile = TileHandler.instance().getTile(tile_id)
self.buttons[button_id].setTile(tile, False)
else: # No tile? reset
Expand Down Expand Up @@ -141,7 +158,6 @@ def _find_index(num):
raise ValueError(f"{num} not found")

button_x, button_y = _find_index(button_id)
print(button_x, button_y)
ret = []
for y in [-1, 0, 1]:
for x in [-1, 0, 1]:
Expand Down
1 change: 1 addition & 0 deletions src/images_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def instance(cls):
def _init(self):
self.CHECKER_IMAGE = QImage("data/img/checker.png").scaled(16, 16)
self.NO_TILE_FOUND = QPixmap("data/img/tile_not_found.png")
self.NO_TILE_FOUND_EMPTY = QPixmap("data/img/tile_not_found_empty.png")

def drawCheckerImage(self, qp: QPainter, width: int, height: int):
for y in range(0, height, self.CHECKER_IMAGE.height()):
Expand Down
5 changes: 2 additions & 3 deletions src/logic/tile_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _encode(self, bits) -> int:
Returns a list of TileConnections possible with the any connection
"""

def getPermutations(self) -> List["TileConnection"]:
def getPossibleNeighborhoods(self) -> List["TileConnection"]:
i = 0
while i < EIGHT_NEIGHBORS and self._neighbors[i] != 2:
i += 1
Expand All @@ -44,13 +44,12 @@ def getPermutations(self) -> List["TileConnection"]:
neighbors = self._neighbors.copy()
for j in [0, 1]:
neighbors[i] = j
ret.extend(TileConnection(neighbors).getPermutations())
ret.extend(TileConnection(neighbors).getPossibleNeighborhoods())
return ret

"""
rotates 45 degree
"""

def rotate45(self) -> "TileConnection":
neighbors = [0] * EIGHT_NEIGHBORS

Expand Down
Loading

0 comments on commit 315b792

Please sign in to comment.