<commit_before>#!/usr/bin/env python
# DungeonDisplay - the View for our dungeon generator
#
# Handles rendering of DungeonMap and its associated Regions and Connections.

import logging
import pygame
import math

from .map import DungeonMap, Region, Connection, Decoration
from .geometry import rotate, to_string

log = logging.getLogger(__name__)

class DungeonDisplay:
    """Class to handle visual rendering of the map"""

    def __init__(self, dungeon_map, (w, h)=(None, None)):
        assert isinstance(dungeon_map, DungeonMap)
        if w is None:
            w = pygame.display.Info().current_w - 100
        if h is None:
            h = pygame.display.Info().current_h - 100
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.dungeon_map = dungeon_map
        log.debug("Initialized {0}".format(self))


    def __str__(self):
        return ("<DungeonDisplay: surface {0}, map {1}>"
                .format(self.surface, self.dungeon_map))


    def resize_display(self, (w, h)):
        """Handle a window resize event by adjusting our display size"""
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)


    def update_bounds(self, (display_w, display_h)=(None, None)):
        """Update the display position and scale prior to drawing the map"""
        log.debug("Recalculating map display bounds")
        # Get the extent of the dungeon
        (x0, y0, x1, y1) = self.dungeon_map.bounds

        # Get the extent of the surface
        if display_w is None:
            (display_w, display_h) = self.surface.get_size()
        log.debug("Base parameters are {0}, {1}"
                  .format(self.dungeon_map.bounds,
                          self.surface.get_size()))

        # Provide 10' of buffer around the edges of the map
        (x0, y0, x1, y1) = (x0 - 10, y0 - 10, x1 + 10, y1 + 10)

        # Figure out how much to scale to make the map fit the display
        map_w = (x1 - x0)
        map_h = (y1 - y0)
        xscale = display_w / map_w
        yscale = display_h / map_h
        # self.display_scale is the number of pixels per foot.
        self.display_scale = math.floor(min(xscale, yscale))
        if self.display_scale < 1:
            self.display_scale = 1
        log.debug("Display scale ({0}, {1}) -> {2}"
                  .format(xscale, yscale, self.display_scale))

        # Center the dungeon in the display as needed
        delta = (display_h / self.display_scale) - map_h
        y0 -= math.floor(delta / 2)
        y1 += math.ceil(delta / 2)
        delta = (display_w / self.display_scale) - map_w
        x0 -= math.floor(delta / 2)
        x1 += math.ceil(delta / 2)

        self.bounds = (x0, y0, x1, y1)

        log.debug("Display bounds are now {0}, scale is {1}"
                  .format(self.bounds, self.display_scale))


    def map_to_screen(self, coords):
        """Convert (x, y) or [(x0, y0), (x1, y1), ...] to the corresponding
        onscreen coordinates"""

        (x0, y0, x1, y1) = self.bounds

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting

        # Handle single point
        if hasattr(coords, "x") and hasattr(coords, "y"):
            return self.map_to_screen((coords.x, coords.y))
        if type(coords) is tuple:
            (x, y) = coords
            return ((x - x0) * self.display_scale,
                    (y1 - y) * self.display_scale)
        # Handle list of points
        output_list = []
        for (x, y) in coords:
            output_list.append(((x - x0) * self.display_scale,
                                (y1 - y) * self.display_scale))
        log.debug("map {0} corresponds to screen {1}"
                  .format(to_string(coords), to_string(output_list)))
        return output_list<commit_msg>Support wider range of display scales while still maintaining pixel accuracy; also keep map grid aligned to top left corner of window<commit_after>#!/usr/bin/env python
# DungeonDisplay - the View for our dungeon generator
#
# Handles rendering of DungeonMap and its associated Regions and Connections.

import logging
import pygame
import math

from .map import DungeonMap, Region, Connection, Decoration
from .geometry import rotate, to_string

log = logging.getLogger(__name__)

class DungeonDisplay:
    """Class to handle visual rendering of the map"""

    def __init__(self, dungeon_map, (w, h)=(None, None)):
        assert isinstance(dungeon_map, DungeonMap)
        # Warning! If you call pygame.display.set_mode() with a (w, h) larger
        # than the window can actually be constructed (due to menu bars,
        # window title bars, dock/taskbar, etc.), pygame will create a smaller
        # window and quietly rescale everything on-the-fly for you.
        # We don't want that!
        # So we make our initial window quite a bit smaller than the display
        # size so that we can reasonably expect it's a valid window.
        if w is None:
            w = pygame.display.Info().current_w - 200
        if h is None:
            h = pygame.display.Info().current_h - 200
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)
        self.dungeon_map = dungeon_map
        log.debug("Initialized {0}".format(self))


    def __str__(self):
        return ("<DungeonDisplay: surface {0}, map {1}>"
                .format(self.surface, self.dungeon_map))


    def resize_display(self, (w, h)):
        """Handle a window resize event by adjusting our display size"""
        self.surface = pygame.display.set_mode((w, h), pygame.RESIZABLE)


    def update_bounds(self, (display_w, display_h)=(None, None)):
        """Update the display position and scale prior to drawing the map"""
        log.debug("Recalculating map display bounds")
        # Get the extent of the dungeon
        (x0, y0, x1, y1) = self.dungeon_map.bounds

        # Get the extent of the surface
        if display_w is None:
            (display_w, display_h) = self.surface.get_size()
        log.debug("Base parameters are {0}, {1}"
                  .format(self.dungeon_map.bounds,
                          self.surface.get_size()))

        # Provide 10' of buffer around the edges of the map
        (x0, y0, x1, y1) = (x0 - 10, y0 - 10, x1 + 10, y1 + 10)

        # Figure out how much to scale to make the map fit the display
        map_w = (x1 - x0)
        map_h = (y1 - y0)
        xscale = display_w / map_w
        yscale = display_h / map_h
        # self.display_scale is the number of pixels per foot.
        # We constrain it such that 5' is always a whole number of pixels
        self.display_scale = math.floor(min(xscale, yscale) * 5) / 5
        if self.display_scale < 1:
            self.display_scale = 1
        log.debug("Display scale ({0}, {1}) -> {2}"
                  .format(xscale, yscale, self.display_scale))

        # Center the dungeon in the display as needed
        # But fudge it a bit to ensure that (x0, y1) is a grid intersection
        delta = (display_h / self.display_scale) - map_h
        y0 = math.floor((y0 - delta/2) / 10) * 10
        y1 = math.ceil((y1 + delta/2) / 10) * 10
        delta = (display_w / self.display_scale) - map_w
        x0 = math.floor((x0 - delta/2) / 10) * 10
        x1 = math.ceil((x1 + delta/2) / 10) * 10

        self.bounds = (x0, y0, x1, y1)

        log.debug("Display bounds are now {0}, scale is {1}"
                  .format(self.bounds, self.display_scale))


    def map_to_screen(self, coords):
        """Convert (x, y) or [(x0, y0), (x1, y1), ...] to the corresponding
        onscreen coordinates"""

        (x0, y0, x1, y1) = self.bounds

        # Note that the screen uses left-handed coordinates while the map
        # uses right-handed coordinates, so we flip the y axis when converting

        # Handle single point
        if hasattr(coords, "x") and hasattr(coords, "y"):
            return self.map_to_screen((coords.x, coords.y))
        if type(coords) is tuple:
            (x, y) = coords
            return ((x - x0) * self.display_scale,
                    (y1 - y) * self.display_scale)
        # Handle list of points
        output_list = []
        for (x, y) in coords:
            output_list.append(((x - x0) * self.display_scale,
                                (y1 - y) * self.display_scale))
        log.debug("map {0} corresponds to screen {1}"
                  .format(to_string(coords), to_string(output_list)))
        return output_list
{'fraction_non_alphanumeric': 0.10842670401493931, 'fraction_numerical': 0.019257703081232494, 'mean_word_length': 2.1784124629080117, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2174114', 'n_tokens_mistral': 2724, 'n_tokens_neox': 2590, 'n_words': 858}