Tools to view, edit and create 7dtd world-maps https://chrani.net/map-tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
9.6 KiB

import math
import pathlib
import numpy as np
from PyQt5.QtCore import (
QThread, pyqtSignal, QObject
)
from PyQt5.QtWidgets import (
QGridLayout
)
from einops import rearrange
from object_region_label import RegionLabel
from tools import (
create_grid_index_from_region_string, create_region_string_from_grid_index
)
class RegionLabelHelper(QObject):
update_region_collection_raw_data_signal = pyqtSignal(str, np.ndarray)
toggle_is_marked_signal = pyqtSignal(RegionLabel)
toggle_is_selected_signal = pyqtSignal(RegionLabel)
region_label_set_raw_data_array = pyqtSignal(str, np.ndarray)
class WorkerUpdateLabelCollection(QThread):
finished = pyqtSignal()
progress = pyqtSignal(int)
def __init__(self, blockshaped_map_data, region_label_helper):
super(QThread, self).__init__()
self.blockshaped_map_data = blockshaped_map_data
self.regions_per_row = blockshaped_map_data.shape[0]
self.offset = int(32 / 2) - int(self.blockshaped_map_data.shape[0] / 2)
self.region_label_helper = region_label_helper
def run(self):
progress_percentage = (100 / self.regions_per_row) / self.regions_per_row
for y in range(0, self.regions_per_row):
self.progress.emit(int((y * (progress_percentage * self.regions_per_row))))
for x in range(0, self.regions_per_row):
self.region_label_helper.region_label_set_raw_data_array.emit(
create_region_string_from_grid_index(x=x + self.offset, y=y + self.offset),
self.blockshaped_map_data[y][x]
)
self.progress.emit(100)
self.finished.emit()
class RegionLabelCollection(QGridLayout):
load_collection_thread = QThread
update_label_worker = WorkerUpdateLabelCollection
label_collection = dict
currently_hovered_label = RegionLabel
blockshaped_map_data = np.array
def __init__(
self, zoom_adjusted_label_size=None,
progress_bar_helper=None, mouse_cursor_helper=None,
world_file_helper=None
):
super(RegionLabelCollection, self).__init__()
self.load_collection_thread = None
self.label_collection = {}
self.zoom_adjusted_label_size = zoom_adjusted_label_size
self.progress_bar_helper = progress_bar_helper
self.mouse_cursor_helper = mouse_cursor_helper
self.world_file_helper = world_file_helper
self.region_label_helper = RegionLabelHelper()
self.init_signals()
self.init_ui()
def init_ui(self):
self.setSpacing(0)
self.setContentsMargins(0, 0, 0, 0)
for y in range(0, 32):
for x in range(0, 32):
self.create_label(
x=x, y=y,
dimensions=self.zoom_adjusted_label_size, mouse_cursor_helper=self.mouse_cursor_helper,
region_label_helper=self.region_label_helper
)
def init_signals(self):
self.mouse_cursor_helper.hovered_label_signal.connect(self.hovered_label_slot)
self.world_file_helper.load_world_file_signal.connect(self.load_world_file_slot)
self.world_file_helper.save_world_file_signal.connect(self.save_world_file_slot)
self.region_label_helper.update_region_collection_raw_data_signal.connect(
self.change_region_label_region_raw_data_slot
)
self.region_label_helper.toggle_is_marked_signal.connect(self.change_region_label_toggle_marked_slot)
self.region_label_helper.toggle_is_selected_signal.connect(self.change_region_label_toggle_selected_slot)
def hovered_label_slot(self, region_label: RegionLabel):
for index, label in self.label_collection.items():
if region_label is not label:
label.remove_graphics_effects()
if region_label is not None:
self.currently_hovered_label = region_label
region_label.update_graphics_effect()
def change_region_label_region_raw_data_slot(self, region_string: str, raw_region_data: np.ndarray):
index_x, index_y = create_grid_index_from_region_string(region_string)
offset = int(32 / 2) - int(self.blockshaped_map_data.shape[0] / 2)
self.blockshaped_map_data[index_y - offset][index_x - offset] = raw_region_data
def change_region_label_toggle_selected_slot(self, region_label: RegionLabel):
for index, label in self.label_collection.items():
label.is_selected = False
label.update_style_effects()
if region_label is not None:
region_label.is_selected = True
region_label.update_style_effects()
def change_region_label_toggle_marked_slot(self, region_label: RegionLabel):
if region_label is not None:
region_label.is_marked = not region_label.is_marked
adjacent_marked_region_labels = self.get_adjacent_marked_region_labels(region_label)
region_label.set_adjacent_marked_region_labels(adjacent_marked_region_labels)
region_label.update_style_effects()
for direction, adjacent_marked_region_label in adjacent_marked_region_labels.items():
neighbors_adjacent_marked_region_labels = self.get_adjacent_marked_region_labels(
adjacent_marked_region_label
)
adjacent_marked_region_label.set_adjacent_marked_region_labels(neighbors_adjacent_marked_region_labels)
adjacent_marked_region_label.update_style_effects()
def get_adjacent_marked_region_labels(self, region_label: RegionLabel):
index_x, index_y = create_grid_index_from_region_string(region_label.region_string)
adjacent_region_labels = {
"top": self.label_collection.get(
create_region_string_from_grid_index(y=index_y - 1, x=index_x), None
),
"right": self.label_collection.get(
create_region_string_from_grid_index(y=index_y, x=index_x + 1), None
),
"bottom": self.label_collection.get(
create_region_string_from_grid_index(y=index_y + 1, x=index_x), None
),
"left": self.label_collection.get(
create_region_string_from_grid_index(y=index_y, x=index_x - 1), None
)
}
adjacent_marked_region_labels = {}
for direction, adjacent_region_label in adjacent_region_labels.items():
if adjacent_region_label is not None and adjacent_region_label.is_marked:
adjacent_marked_region_labels.update({direction: adjacent_region_label})
return adjacent_marked_region_labels
def create_label(self, x=None, y=None, dimensions=None, mouse_cursor_helper=None, region_label_helper=None):
label = RegionLabel(
x=x, y=y,
mouse_cursor_helper=mouse_cursor_helper,
region_label_helper=region_label_helper
)
label.setFixedSize(dimensions, dimensions)
index = create_region_string_from_grid_index(x=x, y=y)
self.addWidget(label, y, x)
self.label_collection.update({index: label})
def change_label_size(self, size):
label = empty = object()
for index, label in self.label_collection.items():
label.setFixedSize(size, size)
if label is not empty:
label.parent().adjustSize()
def clear_viewer_grid(self):
for i in range(self.count()):
self.itemAt(i).widget().remove_label_pixmap()
def update_label_collection_with_raw_map_data(self, raw_map_data, length):
if self.load_collection_thread is None:
self.clear_viewer_grid()
raw_map_data = np.flipud(np.reshape(raw_map_data, (length, length)))
self.blockshaped_map_data = rearrange(raw_map_data, '(x dx) (y dy) -> x y dx dy', dx=512, dy=512)
self.load_collection_thread = QThread()
self.update_label_worker = WorkerUpdateLabelCollection(
self.blockshaped_map_data.copy(), self.region_label_helper
)
self.update_label_worker.moveToThread(self.load_collection_thread)
self.load_collection_thread.started.connect(self.update_label_worker.run)
self.update_label_worker.finished.connect(self.load_collection_thread.quit)
self.update_label_worker.finished.connect(self.update_label_worker.deleteLater)
self.load_collection_thread.finished.connect(self.load_collection_thread.deleteLater)
self.update_label_worker.progress.connect(self.update_label_collection_with_raw_map_data_progress)
self.load_collection_thread.finished.connect(self.update_label_collection_with_raw_map_data_done)
self.load_collection_thread.start()
def update_label_collection_with_raw_map_data_progress(self, percentage):
self.progress_bar_helper.progress_bar_percentage_signal.emit(percentage)
def update_label_collection_with_raw_map_data_done(self):
self.progress_bar_helper.progress_bar_percentage_signal.emit(0)
self.load_collection_thread = None
def load_world_file_slot(self, file_name):
length = int(math.sqrt(pathlib.Path(file_name).stat().st_size / 2))
with open(file_name, 'r') as raw_file:
raw_data_array = np.fromfile(raw_file, dtype=np.uint16)
self.update_label_collection_with_raw_map_data(raw_data_array, length)
def save_world_file_slot(self, file_name):
map_data = rearrange(self.blockshaped_map_data, 'x y dx dy -> (x dx) (y dy)')
with open(file_name, 'w+b') as raw_file:
raw_file.write(
bytearray(np.flipud(map_data))
)