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
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)) |
|
)
|
|
|