added zoom levels, reload, home, cache TTL

This commit is contained in:
Louis Heredero 2024-07-05 22:23:03 +02:00
parent a4249899e9
commit 9a3bbc95d9
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7
3 changed files with 55 additions and 22 deletions

View File

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

View File

@ -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,11 +649,18 @@ class Editor:
if len(path.strip()) == 0:
path = last_path
self.graph = Graph.load(path)
if save_config:
self.config.LAST_OPENED_FILE = path
self.config.save()
self.dirty = False
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):

View File

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