added zoom levels, reload, home, cache TTL
This commit is contained in:
parent
a4249899e9
commit
9a3bbc95d9
@ -5,6 +5,7 @@ import os.path
|
||||
class Config:
|
||||
LAST_OPENED_FILE = ""
|
||||
AUTOSAVE_INTERVAL = 5 * 60 * 1000
|
||||
CACHE_TTL = 10
|
||||
|
||||
def __init__(self, path: str):
|
||||
self._path: str = path
|
||||
|
@ -3,12 +3,12 @@ from enum import Enum, auto
|
||||
from math import floor
|
||||
from typing import Optional
|
||||
|
||||
import platformdirs
|
||||
import pygame
|
||||
|
||||
from src.config import Config
|
||||
from src.image_handler import ImageHandler
|
||||
from src.graph.graph import Graph
|
||||
from src.image_handler import ImageHandler
|
||||
from src.utils.paths import CONFIG_DIR, CACHE_DIR
|
||||
|
||||
|
||||
class Editor:
|
||||
@ -17,13 +17,11 @@ class Editor:
|
||||
WIDTH: int = 800
|
||||
HEIGHT: int = 600
|
||||
MAP_SIZE: int = 1024
|
||||
CACHE_DIR: str = platformdirs.user_cache_dir(appname=APP_NAME, appauthor=APP_AUTHOR, ensure_exists=True)
|
||||
CONFIG_DIR: str = platformdirs.user_config_dir(appname=APP_NAME, appauthor=APP_AUTHOR, ensure_exists=True)
|
||||
CONFIG_PATH: str = os.path.join(CONFIG_DIR, "config.json")
|
||||
MAPS_DIR: str = os.path.join(CACHE_DIR, "maps")
|
||||
AUTOSAVE_PATH: str = os.path.join(CACHE_DIR, "AUTOSAVE.txt")
|
||||
AUTOSAVE_EVENT: int = pygame.event.custom_type()
|
||||
ZOOMS: tuple[float] = (0.25, 0.5, 1, 2, 4)
|
||||
ZOOMS: tuple[float] = tuple(2**p for p in range(-6, 7))
|
||||
CROSSHAIR_SIZE: int = 10
|
||||
|
||||
def __init__(self):
|
||||
@ -34,19 +32,21 @@ class Editor:
|
||||
self.win: pygame.Surface = pygame.display.set_mode([self.width, self.height], pygame.RESIZABLE)
|
||||
pygame.display.set_caption("Lycacraft Map Editor")
|
||||
self.center: list[int] = [0, 0]
|
||||
self.zoom_i: int = 2
|
||||
self.zoom_i: int = self.ZOOMS.index(1)
|
||||
self.zoom: float = self.ZOOMS[self.zoom_i]
|
||||
self.running: bool = False
|
||||
self.image_handler: ImageHandler = ImageHandler(self.MAPS_DIR, self.MAP_SIZE)
|
||||
self.image_handler: ImageHandler = ImageHandler(self.MAPS_DIR, self.MAP_SIZE, self.config.CACHE_TTL)
|
||||
self.clock: pygame.time.Clock = pygame.time.Clock()
|
||||
self.left_drag_pos: Optional[tuple[int, int]] = None
|
||||
self.mid_drag_pos: Optional[tuple[int, int]] = None
|
||||
self.font: pygame.font.Font = pygame.font.SysFont("Ubuntu", 20)
|
||||
self.loading_font: pygame.font.Font = pygame.font.SysFont("Ubuntu", 30)
|
||||
self.zooms_texts: list[pygame.Surface] = list(map(
|
||||
lambda z: self.font.render(str(z), True, (255, 255, 255)),
|
||||
self.ZOOMS
|
||||
))
|
||||
self.zooms_texts: list[pygame.Surface] = []
|
||||
for zoom in self.ZOOMS:
|
||||
txt = str(zoom)
|
||||
if zoom < 1:
|
||||
txt = f"1/{int(1/zoom):d}"
|
||||
self.zooms_texts.append(self.font.render(txt, True, (255, 255, 255)))
|
||||
self.is_renaming_node: bool = False
|
||||
self.state: State = State.STOPPING
|
||||
self.graph = Graph()
|
||||
@ -61,6 +61,7 @@ class Editor:
|
||||
self.original_move_pos: Optional[tuple[int, int]] = None
|
||||
self.move_old_poses: Optional[dict[int, tuple[int, int]]] = None
|
||||
self.dirty: bool = False
|
||||
self.loading_bg: pygame.Surface = pygame.Surface([self.width, self.height])
|
||||
pygame.time.set_timer(self.AUTOSAVE_EVENT, self.config.AUTOSAVE_INTERVAL)
|
||||
|
||||
if os.path.exists(self.AUTOSAVE_PATH):
|
||||
@ -72,7 +73,7 @@ class Editor:
|
||||
def mainloop(self) -> None:
|
||||
self.state = State.LOADING
|
||||
while self.state != State.STOPPING:
|
||||
caption = f"Lycacraft Map Editor - {self.clock.get_fps():.2f}fps"
|
||||
caption = f"Lycacraft Map Editor - {self.clock.get_fps():.2f}fps - {self.image_handler.size} images"
|
||||
if self.dirty:
|
||||
caption += " (unsaved)"
|
||||
pygame.display.set_caption(caption)
|
||||
@ -87,6 +88,7 @@ class Editor:
|
||||
if self.original_move_pos is not None:
|
||||
self.move_poses()
|
||||
self.render()
|
||||
self.image_handler.clean()
|
||||
self.clock.tick(30)
|
||||
|
||||
def quit(self) -> None:
|
||||
@ -138,6 +140,10 @@ class Editor:
|
||||
if len(self.selected_nodes) == 1:
|
||||
self.typing_text = self.graph.nodes[self.selected_nodes[0]].name
|
||||
self.is_renaming_node = True
|
||||
elif event.key == pygame.K_HOME:
|
||||
self.center = [0, 0]
|
||||
elif event.key == pygame.K_F5:
|
||||
self.reload()
|
||||
elif event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_m:
|
||||
if self.original_move_pos is not None:
|
||||
@ -198,7 +204,7 @@ class Editor:
|
||||
self.mid_drag_pos = mpos
|
||||
|
||||
def render(self) -> None:
|
||||
self.win.fill((0, 0, 0))
|
||||
self.win.fill((50, 50, 50))
|
||||
off_x = (self.center[0] * self.zoom) % self.MAP_SIZE
|
||||
off_y = (self.center[1] * self.zoom) % self.MAP_SIZE
|
||||
|
||||
@ -254,13 +260,13 @@ class Editor:
|
||||
pygame.display.flip()
|
||||
|
||||
def render_zoom_slider(self) -> None:
|
||||
zoom_height = self.height * 0.2
|
||||
zoom_r = self.height / 80
|
||||
zoom_space = zoom_r * 4
|
||||
zoom_height = zoom_space * (len(self.ZOOMS) - 1)
|
||||
zoom_h_margin = self.width * 0.02
|
||||
zoom_v_margin = self.height * 0.05
|
||||
zoom_x = self.width - zoom_h_margin
|
||||
zoom_y = self.height - zoom_v_margin - zoom_height
|
||||
zoom_space = zoom_height / 4
|
||||
zoom_r = zoom_space / 4
|
||||
zoom_width = max(s.get_width() for s in self.zooms_texts) + 2 * zoom_r + 5
|
||||
pygame.draw.rect(self.win, (80, 80, 80), [
|
||||
zoom_x + zoom_r - zoom_width - 5,
|
||||
@ -277,6 +283,7 @@ class Editor:
|
||||
|
||||
def render_loading(self) -> None:
|
||||
self.win.fill((0, 0, 0))
|
||||
self.win.blit(self.loading_bg, [0, 0])
|
||||
count = self.image_handler.count
|
||||
total = self.image_handler.total
|
||||
txt = self.loading_font.render(f"Loading maps - {count}/{total}", True, (255, 255, 255))
|
||||
@ -642,12 +649,19 @@ class Editor:
|
||||
if len(path.strip()) == 0:
|
||||
path = last_path
|
||||
|
||||
if os.path.exists(path):
|
||||
self.graph = Graph.load(path)
|
||||
if save_config:
|
||||
self.config.LAST_OPENED_FILE = path
|
||||
self.config.save()
|
||||
self.dirty = False
|
||||
|
||||
def reload(self) -> None:
|
||||
self.state = State.LOADING
|
||||
self.loading_bg = self.win.copy()
|
||||
del self.image_handler
|
||||
self.image_handler = ImageHandler(self.MAPS_DIR, self.MAP_SIZE, self.config.CACHE_TTL)
|
||||
|
||||
|
||||
class State(Enum):
|
||||
STOPPING = auto()
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from math import floor
|
||||
from typing import Optional
|
||||
|
||||
@ -7,10 +8,13 @@ import pygame
|
||||
|
||||
|
||||
class ImageHandler:
|
||||
def __init__(self, maps_dir: str, base_size: int):
|
||||
def __init__(self, maps_dir: str, base_size: int, ttl: int):
|
||||
self.maps_dir: str = maps_dir
|
||||
self.base_size: int = base_size
|
||||
self.ttl: int = ttl
|
||||
self.cache: dict = {}
|
||||
self.history: dict[tuple[float, tuple[int, int]], float] = {}
|
||||
self.size: int = 0
|
||||
self.count: int = 0
|
||||
self.total: int = 0
|
||||
self.loading: bool = False
|
||||
@ -29,6 +33,7 @@ class ImageHandler:
|
||||
name, x, y = path.split(".")[0].split("_")
|
||||
cache[(int(x), int(y))] = pygame.image.load(fullpath).convert_alpha()
|
||||
self.count += 1
|
||||
self.size += 1
|
||||
|
||||
self.cache = {
|
||||
1: cache
|
||||
@ -65,6 +70,19 @@ class ImageHandler:
|
||||
img = pygame.transform.scale_by(img, zoom)
|
||||
|
||||
cache[pos] = img
|
||||
self.size += 1
|
||||
|
||||
self.cache[zoom] = cache
|
||||
self.history[(zoom, pos)] = time.time()
|
||||
return cache[pos]
|
||||
|
||||
def clean(self) -> None:
|
||||
t = time.time()
|
||||
new_history = {}
|
||||
for (zoom, pos), t0 in self.history.items():
|
||||
if zoom != 1 and t0 + self.ttl < t:
|
||||
del self.cache[zoom][pos]
|
||||
self.size -= 1
|
||||
else:
|
||||
new_history[(zoom, pos)] = t0
|
||||
self.history = new_history
|
||||
|
Loading…
Reference in New Issue
Block a user