Python Forum
[PyGame] Zooming on mouse position calculation
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Zooming on mouse position calculation
#1
HI :) ! I have been trying to create a simple zoom on point navigation with pygame, and after endless googling sessions, this is as close as I could get. The problem is that when I change the mouse position in a deeper zoom level, it shifts the image a bit to the side. I am probably missing some other value conversion, and unfortunately I do not have enough brain power to understand where or how, so any help would be greatly appreciated.

import pygame
from pygame.locals import*

pygame.init()
clock = pygame.time.Clock()

screen_size = 1920, 1080
aspect_ratio = 16,9
screen = pygame.display.set_mode((screen_size[0], screen_size[1]))
pygame.display.set_caption('Zooooom')

image_path = 'C:/world map HD.jpg'
original_image = pygame.image.load(image_path).convert()

zoom = 0 
zoom_limit = screen_size[0] / aspect_ratio[0] - 1
pos = [0,0]

running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:

            if event.button == 4:
                if zoom < zoom_limit:

                    this_crop_distance = zoom * aspect_ratio[0], zoom * aspect_ratio[1]
                    next_crop_distance = (zoom + 1) * aspect_ratio[0], (zoom + 1) * aspect_ratio[1]

                    this_zoom_resolution = screen_size[0] - this_crop_distance[0], screen_size[1] - this_crop_distance[1]
                    next_zoom_resolution = screen_size[0] - next_crop_distance[0], screen_size[1] - next_crop_distance[1]

                    factor = this_zoom_resolution[0] / next_zoom_resolution[0]

                    updated_x = (event.pos[0] - pos[0]) * (factor - 1)
                    updated_y = (event.pos[1] - pos[1]) * (factor - 1)

                    pos[0] += updated_x
                    pos[1] += updated_y

                    zoom += 1

            elif event.button == 5:
                if zoom > 0:
                    
                    this_crop_distance = zoom * aspect_ratio[0], zoom * aspect_ratio[1]
                    next_crop_distance = (zoom-1) * aspect_ratio[0], (zoom-1) * aspect_ratio[1]

                    this_zoom_resolution = screen_size[0] - this_crop_distance[0], screen_size[1] - this_crop_distance[1]
                    next_zoom_resolution = screen_size[0] - next_crop_distance[0], screen_size[1] - next_crop_distance[1]

                    factor = this_zoom_resolution[0] / next_zoom_resolution[0]

                    updated_x = (event.pos[0] - pos[0]) * (factor - 1)
                    updated_y = (event.pos[1] - pos[1]) * (factor - 1)

                    pos[0] += updated_x
                    pos[1] += updated_y

                    zoom -= 1


    crop_distance = zoom * aspect_ratio[0], zoom * aspect_ratio[1]
    cropped_region = pos[0], pos[1] , screen_size[0] - crop_distance[0], screen_size[1] - crop_distance[1]

    blit_output = pygame.transform.scale(original_image.subsurface(cropped_region), screen_size)
    screen.blit(blit_output, (0,0))

    pygame.display.update()
    clock.tick(60)

pygame.quit()
quit()
Reply
#2
Example. Still needs a little work.
import pygame

class Zoom:
    def __init__(self, area, speed, image):
        # Multiply by 2 for smooth rect inflate.
        self.speed = speed * 2
        self.image = image
        self.area = area
        self.inflate = 0

    def drift(self, screen_rect, mpos, inward):
        if inward:
            d = (pygame.Vector2(mpos) - screen_rect.center).normalize()
        else:
            d = (pygame.Vector2(screen_rect.center) - mpos).normalize()

        d *= self.speed
        self.area.move_ip(int(d.x), int(d.y))

    def reset(self, screen_rect):
        self.inflate = 0
        self.area = screen_rect.copy()
        return self.image.subsurface(self.area)

    def zoom_in(self, screen_rect, event):
        self.drift(screen_rect, event.pos, True)
        self.inflate -= self.speed
        rect = self.area.inflate(self.inflate, self.inflate)
        rect.clamp_ip(self.image.get_rect())

        return pygame.transform.scale(self.image.subsurface(rect), screen_rect.size)

    def zoom_out(self, screen_rect, event):
        self.drift(screen_rect, event.pos, False)
        self.inflate += self.speed
        rect = self.area.inflate(self.inflate, self.inflate)
        rect.clamp_ip(self.image.get_rect())

        if not self.image.get_rect().contains(rect):
            return self.zoom_in(screen_rect, event)

        return pygame.transform.scale(self.image.subsurface(rect), screen_rect.size)

def create_map(size, width, colors):
    surface = pygame.Surface((size, size))
    area = pygame.Rect(0, 0, width, width)
    for x in range(0, size, width):
        for y in range(0, size, width):
            area.topleft = x, y
            color = colors[(x//width + y//width) % 2]
            surface.fill(color, area)

    return surface

def main(caption, width, height, fps=60, flags=0):
    pygame.display.set_caption(caption)
    surface = pygame.display.set_mode((width, height), flags)
    rect = surface.get_rect()
    clock = pygame.time.Clock()
    running = True
    delta = 0
    fps = fps

    worldmap = create_map(1000, 50, ('navy', 'dodgerblue'))
    zoom = Zoom(rect.copy(), 5, worldmap)
    area = worldmap.subsurface(zoom.area)

    # Main Loop
    while running:
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 4:
                    area = zoom.zoom_in(rect, event)
                elif event.button == 5:
                    area = zoom.zoom_out(rect, event)
                elif event.button == 1:
                    area = zoom.reset(rect)

            elif event.type == pygame.QUIT:
                running = False

        surface.fill('black')
        surface.blit(area, (0, 0))
        pygame.display.flip()
        delta = clock.tick(fps)

pygame.init()
main("Zoom Example", 800, 600)
pygame.quit()
99 percent of computer problems exists between chair and keyboard.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020