1
2
3
4
5
6
7
8 from __future__ import division
9 import os
10 import interpolaciones
11 import sys
12 import subprocess
13 import math
14 import uuid
15 import pilas
16
17
18 PATH = os.path.dirname(os.path.abspath(__file__))
19
20
22 """Indica si un objeto se comportará como una interpolación.
23
24 :param an_object: El objeto a consultar.
25 """
26 return isinstance(an_object, interpolaciones.Interpolacion)
27
28
30 """Busca la ruta a un archivo de recursos.
31
32 Los archivos de recursos (como las imagenes) se buscan en varios
33 directorios (ver docstring de image.load), así que esta
34 función intentará dar con el archivo en cuestión.
35
36 :param ruta: Ruta al archivo (recurso) a inspeccionar.
37 """
38
39 dirs = ['./', os.path.dirname(sys.argv[0]), 'data', PATH, PATH + '/data']
40
41
42 for x in dirs:
43 full_path = os.path.join(x, ruta)
44
45
46 if os.path.exists(full_path):
47 return full_path
48
49
50 raise IOError("El archivo '%s' no existe." %(ruta))
51
52
54 """Indica si pilas se ha ejecutado desde una consola interactiva de python."""
55 import sys
56 try:
57 cursor = sys.ps1
58 return True
59 except AttributeError:
60 try:
61 in_ipython = sys.ipcompleter
62 return True
63 except AttributeError:
64 if sys.stdin.__class__.__module__.startswith("idle"):
65 return True
66
67 return False
68
70 """Retorna la distancia entre dos numeros.
71
72 >>> distancia(30, 20)
73 10
74
75 :param a: Un valor numérico.
76 :param b: Un valor numérico.
77 """
78 return abs(b - a)
79
81 """Retorna la distancia entre dos puntos en dos dimensiones.
82
83 :param x1: Coordenada horizontal del primer punto.
84 :param y1: Coordenada vertical del primer punto.
85 :param x2: Coordenada horizontal del segundo punto.
86 :param y2: Coordenada vertical del segundo punto.
87 """
88 return math.sqrt(distancia(x1, x2) ** 2 + distancia(y1, y2) ** 2)
89
91 """Retorna la distancia en pixels entre dos actores.
92
93 :param a: El primer actor.
94 :param b: El segundo actor.
95 """
96 return distancia_entre_dos_puntos((a.x, a.y), (b.x, b.y))
97
111
113 """Retorna True si dos actores estan en contacto.
114
115 :param a: Un actor.
116 :param b: El segundo actor a verificar.
117 """
118 return distancia_entre_dos_actores(a, b) < a.radio_de_colision + b.radio_de_colision
119
121 """Decorador que se aplica a un metodo para que permita animaciones de interpolaciones.
122
123 Ejemplo::
124
125 @interpolable
126 def set_x(self, valor_x):
127 [...]
128
129 :param f: Método sobre el que va a trabajar el interpolador.
130 """
131
132 def inner(*args, **kwargs):
133 value = args[1]
134
135
136
137
138 if isinstance(value, tuple) and len(value) == 2:
139 duracion = value[1]
140 value = value[0]
141 else:
142 duracion = 1
143
144 if isinstance(value, list):
145 value = interpolar(value, duracion=duracion, tipo='lineal')
146 elif isinstance(value, xrange):
147 value = interpolar(list(value), duracion=duracion, tipo='lineal')
148
149 if es_interpolacion(value):
150 value.apply(args[0], function=f.__name__)
151 else:
152 f(args[0], value, **kwargs)
153
154 return inner
155
157 """Convierte una coordenada de pantalla a una coordenada dentro del motor de física.
158
159 :param x: Coordenada horizontal.
160 :param y: Coordenada vertical.
161 """
162 dx, dy = pilas.mundo.motor.centro_fisico()
163 return (x + dx, dy - y)
164
169
171 """Imprime una lista de todos los actores en la escena sobre la consola."""
172 todos = pilas.escena_actual().actores
173
174 print "Hay %d actores en la escena:" %(len(todos))
175 print ""
176
177 for s in todos:
178 print "\t", s
179
180 print ""
181
183 """Retorna el ángulo entro dos puntos de la pantalla.
184
185 :param punto_a: Una tupla con la coordenada del primer punto.
186 :param punto_b: Una tupla con la coordenada del segundo punto.
187 """
188 (x, y) = punto_a
189 (x1, y1) = punto_b
190 return math.degrees(math.atan2(y1 - y, x1 -x))
191
195
199
201 """Calcula el tiempo que se tardará en recorrer una distancia en
202 pixeles con una velocidad constante.
203
204 :param distancia_en_pixeles: La longitud a recorrer medida en pixels.
205 :param velocidad: La velocida medida en pixels.
206 """
207 if (pilas.mundo.motor.canvas.fps.cuadros_por_segundo_numerico > 0):
208 return (distancia_en_pixeles / (pilas.mundo.motor.canvas.fps.cuadros_por_segundo_numerico * velocidad))
209 else:
210 return 0
211
212 -def interpolar(valor_o_valores, duracion=1, demora=0, tipo='lineal'):
213 """Retorna un objeto que representa cambios de atributos progresivos.
214
215 El resultado de esta función se puede aplicar a varios atributos
216 de los actores, por ejemplo:
217
218 >>> bomba = pilas.actores.Bomba()
219 >>> bomba.escala = pilas.interpolar(3)
220
221 Esta función también admite otros parámetros cómo:
222
223 :param duracion: es la cantidad de segundos que demorará toda la interpolación.
224 :param demora: cuantos segundos se deben esperar antes de iniciar.
225 :param tipo: es el algoritmo de la interpolación, puede ser 'lineal'.
226 """
227
228 import interpolaciones
229
230 algoritmos = {
231 'lineal': interpolaciones.Lineal,
232 'aceleracion_gradual': interpolaciones.AceleracionGradual,
233 'desaceleracion_gradual': interpolaciones.DesaceleracionGradual,
234 'rebote_inicial': interpolaciones.ReboteInicial,
235 'rebote_final': interpolaciones.ReboteFinal,
236 'elastico_inicial': interpolaciones.ElasticoInicial,
237 'elastico_final': interpolaciones.ElasticoFinal
238 }
239
240 if algoritmos.has_key(tipo):
241 clase = algoritmos[tipo]
242 else:
243 raise ValueError("El tipo de interpolacion %s es invalido" %(tipo))
244
245
246 if not isinstance(valor_o_valores, list):
247 valor_o_valores = [valor_o_valores]
248
249 return clase(valor_o_valores, duracion, demora)
250
251
253 """Deteiene una interpolación iniciada en un campo de un objeto.
254
255 >>> pilas.utils.deneter_interpolacion(actor, 'y')
256
257 :param objeto: Actor del que se desea detener al interpolacion.
258 :para propiedad: Cadena de texto que indica la propiedad del objeto cuya interpolación se desea terminar.
259 """
260 setter = 'set_' + propiedad
261 try:
262 getattr(objeto, setter)
263 pilas.escena_actual().tweener.removeTweeningFromObjectField(objeto, setter)
264 except:
265 print "El obejto %s no tiene esa propiedad %s" %(objeto.__class__.__name__, setter)
266
270
275
277 """Informa el ancho y alto que necesitara un texto para imprimirse.
278
279 :param texto: La cadena de texto que se quiere imprimir.
280 """
281 return pilas.mundo.motor.obtener_area_de_texto(texto)
282
284 """Imprime pruebas en pantalla para detectar si pilas tiene todas las dependencias instaladas."""
285 print "Realizando pruebas de dependencias:"
286 print ""
287
288 print "Box 2D:",
289
290 if pilas.fisica.obtener_version().startswith("2.1"):
291 print "OK, versión", pilas.fisica.obtener_version()
292 else:
293 print "Error -> la versión está obsoleta, instale una versión de la serie 2.1"
294
295 print "pyqt:",
296
297 try:
298 from PyQt4 import Qt
299 print "OK, versión", Qt.PYQT_VERSION_STR
300 except ImportError:
301 print "Error -> no se encuentra pyqt."
302
303 print "pyqt con aceleracion:",
304
305 try:
306 from PyQt4 import QtOpenGL
307 from PyQt4.QtOpenGL import QGLWidget
308 print "OK"
309 except ImportError:
310 print "Error -> no se encuentra pyqt4gl."
311
312 print "PIL para soporte de jpeg (opcional):",
313
314 try:
315 from PIL import Image
316 print "OK"
317 except ImportError:
318 print "Cuidado -> no se encuentra PIL."
319
321 """Imprime en pantalla el codigo fuente asociado a un objeto.
322
323 :param objeto: El objeto que se quiere inspeccionar.
324 :param imprimir: Un valor True o False indicando si se quiere imprimir directamente sobre la pantalla.
325 :param retornar: Un valor True o False indicando si se quiere obtener el código como un string.
326 """
327 import inspect
328
329 try:
330 codigo = inspect.getsource(objeto.__class__)
331 except TypeError:
332 codigo = inspect.getsource(objeto)
333
334 if imprimir:
335 print codigo
336
337 if retornar:
338 return codigo
339
341 """Genera un identificador único."""
342 return str(uuid.uuid4())
343
345 """Intenta abrir un archivo con la herramienta recomenda por el sistema operativo.
346
347 :param ruta_al_archivo: La ruta al archivo que se quiere abrir.
348 """
349 if sys.platform.startswith('darwin'):
350 subprocess.call(('open', ruta_al_archivo))
351 elif os.name == 'nt':
352 os.startfile(ruta_al_archivo)
353 elif os.name == 'posix':
354 subprocess.call(('xdg-open', ruta_al_archivo))
355
357 """Coloca la ventana o widget directamente en el centro del escritorio.
358
359 :param widget: Widget que representa la ventana.
360 """
361 from PyQt4 import QtGui
362 desktop = QtGui.QApplication.desktop()
363 widget.move(desktop.screen().rect().center() - widget.rect().center())
364
366 """Inicia la descarga de una archivo desde Internet.
367
368 :param parent: El widget que será padre de la ventana.
369 :param url: La URL desde donde se descargará el archivo.
370 :param archivo_destino: La ruta en donde se guardará el archivo.
371 """
372 import descargar
373 ventana = descargar.Descargar(parent, url, archivo_destino)
374 ventana.show()
375
387
389 """Permite habilitar un breakpoint para depuracion una vez inicializado pilas."""
390 from PyQt4.QtCore import pyqtRemoveInputHook
391 from pdb import set_trace
392 pyqtRemoveInputHook()
393 set_trace()
394
396 """Muestra un mensaje de error y termina con la ejecución de pilas.
397
398 :param motivo: Un mensaje que explica el problema o la razón del cierre de pilas.
399 """
400 from PyQt4 import QtGui
401 app = QtGui.QApplication(sys.argv[:1])
402 app.setApplicationName("pilas-engine error")
403 main_window = QtGui.QMainWindow()
404 main_window.show()
405 main_window.raise_()
406 QtGui.QMessageBox.critical(main_window, "Error", motivo)
407 app.exit()
408 sys.exit(1)
409
411 """Obtiene la ruta del archivo a ejecutar desde la linea de argumentos del programa."""
412 import sys, copy
413
414 argv = copy.copy(sys.argv)
415
416 if '-i' in argv:
417 argv.remove('-i')
418
419 return " ".join(sys.argv[1:])
420