Arquivo para Uncategorized

Código de ordenação de voxels

Prometi, aqui está. O mestrado começou e percebi que ele vai destruir o que resta de minha alma =D

Em breve faço um post sobre colisão de polígonos convexos.

EDIT: Sugestões para otimização são bem-vindas!

import math

def trans(xa, ya, xb, yb, voxel):
    """
    Given a start and end point, returns the list of voxels
    that must be checked against collisions, in priority order,
    as a list of (voxel_x, voxel_y) tuples.

    A brief study about parametric line equations will help!

    Implements the method seem at: http://www.cs.yorku.ca/~amana/research/grid.pdf

    @param xa: The start x coord
    @param ya: The start y coord
    @param xb: The end x coord
    @param yb: The end y coord
    @param voxel: The voxel width and length (i.e. for 16x16 tiles it would be 16)

    @author: Paolo victor, paolovictor@gmail.com
    """
    # Getting start and end x and y voxels
    xa_g, ya_g = xa // voxel, ya // voxel
    xb_g, yb_g = xb // voxel, yb // voxel

    # Base cases: same y voxel, same x voxel
    if xa_g == xb_g or ya_g == yb_g:
        # Getting x and y increments
        if xa_g < xb_g: x_inc = 1
        else: x_inc = -1

        if ya_g < yb_g: y_inc = 1
        else: y_inc = -1

        # If it's in same horizontal voxel, test if it's in the same
        # vertical voxel. Return a straight vertical line otherwise
        if xa_g == xb_g:
            if ya_g == yb_g:
                return [(xa_g, ya_g)]

            return [(xa_g, i) for i in range(ya_g, yb_g + y_inc, y_inc)]
        elif ya == yb: # Similar for being in the same vertical voxel
            return [(i, ya_g) for i in range(xa_g, xb_g + x_inc, x_inc)]

    # Usual case: different start and end voxels
    xbxa = xb - xa
    ybya = yb - ya

    # Evaluating horizontal increment
    x_dir = float((xbxa) // abs(xbxa))
    y_dir = float((ybya) // abs(ybya))

    # Initializing and starting loop
    xp, yp = xa, ya

    result = []
    r = 0.0
    while r <= 1.0: # Goes through the whole vector
        # Add current voxel to results
        xp_g, yp_g = xp // voxel, yp // voxel
        result.append((xp_g, yp_g))

        # Evaluate horizontal and vertical increase
        delta_rx = ((xp_g + x_dir) * voxel - xp) / xbxa
        delta_ry = ((yp_g + y_dir) * voxel - yp) / ybya

        # Choose the smaler increment
        if delta_rx < delta_ry:
            xp += delta_rx * xbxa
            yp += delta_rx * ybya
            r += delta_rx
        else:
            xp += delta_ry * xbxa
            yp += delta_ry * ybya
            r += delta_ry

    return result

Deixe um comentário

Python, lento?

Primeiramente, uma breve paráfrase do nosso colega Dilbert:

GAAAH!

Em um acidente de percurso, perdi todo o código-fonte da interface gráfica do editor. Ele tinha botões, checkboxes, janelas com “drag-and-drop”, tudo OO e fácil de usar. Era lindo.

Era.

Voltando à programação normal, vamos conversar um pouco sobre engines de plataforma. Mais especificamente, engines baseadas em tiles. A questão é: como mover o personagem e detectar colisões entre ele e o cenário?

Uma abordagem simples seria, a cada atualização, mover o personagem D pixels na direção desejada. Se a posição for inválida, o personagem é reposicionado.

Movimento e colisão em engine de plataforma 1

Mas… e se o movimento foi tão acentuado que impossibilitou a detecção?

Movimento e colisão em engine de plataforma 2

Uma solução é, dada uma reta entre o ponto de origem do personagem e o ponto de destino, testar a colisão com todos os blocos cuja intersecção com a reta não seja nula. Note que, para esse método funcionar, os blocos devem ser ordenados por ordem de intersecção.

Esse cara resolveu esse problema. Ele diz que o algoritmo é simples, rápido, etc etc (e realmente é!). Depois de relembrar as aulas de geometria vetorial, parti para implementar o algoritmo em Python. Vocês já devem imaginar o resultado. Embora deveria, ainda não trabalhei em uma implementação em C para comparar a performance, mas creio que isso não seja realmente necessário :-)

Entretanto, é suficiente. No meu teste, executei o algoritmo para 1000 pares de pontos de origem em destino, contidos entre (0,0) e (1000,1000). Isso seria como ter 1000 objetos se movendo a uma velocidade de até 1000 pixels por atualização de lógica. Com 30 atualizações de lógica por segundo, daria uns 30000 pixels por segundo. É um bocado, e provavelmente não vai acontecer. O resultado?

0.127067825659 segundos

Mudando para um cenário menos megalomaníacio: pontos entre (0,0) e (16,16), 128 objetos:

0.00307580991439 segundos

E mesmo se acontecer de se tornar um gargalo de desempenho, posso partir para a ignorância e usar artifícios com o psyco e bibliotecas como o mpmath.

Em breve faço um post com o código-fonte.

Deixe um comentário

File selector em Python + Tkinter

Antes de partir para a violência e decidir programar uma biblioteca própria, decidi tentar usar duas bibliotecas conhecidas: PyGTK e Tkinter, que é uma abstração simples sob o Tcl/Tk para Python.

Não tenho opinião formada sobre o PyGTK, até porque não consegui sequer usá-lo. Apesar de seguir as instruções de instalação à risca, o Python sempre acusa um erro críptico ao tentar importar uma das bibliotecas necessárias.

O Tkinter, como já disse antes, é uma camada simples sob o Tcl/Tk. Desta forma, não espere nada que lembre a simplicidade de Python. Na verdade, há muita magia-negra envolvida. Até para abrir um file selector você tem que fazer o bind manual das ações “Ok” e “Cancel”. Está bem que algum cidadão poderia querer que o botão “Ok” fizesse mais alguma coisa além de confirmar a escolha, mas a falta de uma opção padrão me faz sentir como se ainda estivéssemos em 1997.

E não preciso nem falar sobre a “documentação”. Exemplos intuitivos de uso? Hah!

Apesar dos pesares, o Tkinter tem algumas funcionalidades que dariam um bom trabalho se eu decidisse implementá-las. Assim, o usarei para alguns itens específicos, como para selecionar um arquivo para edição.

Para ajudar a manter minha sanidade, implementei mais uma camada acima do Tkinter, que implementa um seletor de arquivos. Eu queria algo que permitisse que eu usasse apenas um método para selecionar um arquivo. Algo como:

caminho_do_arquivo = open_file_selector()

O resultado é:

# Wrapper for the Python's Tix ExFileSelector dialog. It handles
# the annoying handle assignment and TK black magic, offering
# a bare-bones OO file selector implementation.
#
# Author: Paolo Victor, paolovictor@gmail.com
import Tix
from Tkinter import *
from Tkconstants import *

class TKSelector:
    def __file_selected__(self, path):
        # Sets path, closes window
        self.__path__ = path
        self.__root__.destroy()

    def __cancel_button_clicked__(self, action):
        # Unsets path, closes window
        self.__path__ = None
        self.__root__.destroy()

    def open(self, title="Select a file", baseDir=None):
        # Resetting path
        self.__path__ = None

        # Initializing root panel
        self.__root__ = Tix.Tk()
        self.__root__.wm_resizable(0, 0)
        self.__root__.wm_title(title)

        # Initializing  selector
        self.__selector__ = Tix.ExFileSelectBox(self.__root__)
        self.__selector__.cancel.bind("", self.__cancel_button_clicked__)        

        configuration = {"command": self.__file_selected__}
        if baseDir: configuration["dir"] = baseDir
        self.__selector__.configure(configuration)

        self.__selector__.pack()

        # Displaying selector
        self.__selector__.mainloop(0)

        return self.__path__

# Convenience method =]
def open_file_selector(title="Select a file", baseDir=None):
    selector = TKSelector()
    return selector.open(title, baseDir)

É no mínimo irônico que eu tenha reclamado da documentação do Tkinter, para logo depois mostrar um código pouco documentado :) , mas entendo que os nomes das variáveis são auto-descritivos.

Exemplo de uso, se o código estiver em um módulo chamado “utils”:

import utils

caminho_do_arquivo = utils.open_file_selector("Selecione um arquivo")

Este código abre um seletor de arquivos com o título “Selecione um arquivo”, cujo caminho inicial é o diretório de execução do processo e que retorna o nome do arquivo, se este for selecionado, ou “None”, se a seleção foi cancelada.

Comentários (1)