Noticias

Importante: ¡Si quieres publicar tu juego no olvides leer este tema!

Comunidad Game Maker

Bienvenid@ a la comunidad hispana de Game Maker. Nuestro objetivo es crear videojuegos y dar soporte en castellano de GM. Para mejorar nuestro servicio hemos implantado, como adicion al reglamento general, algunas normas especificas en los subforos más comunes. ¡No olvides informarte antes de participar!.

Autor Tema: Problema con redondeo [solucionado]  (Leído 478 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Clamud

en: Agosto 11, 2015, 03:59:41 am
Hola, hice un programa para encontrar la circunferencia que pasa por 3 puntos (usando el método de sistema de ecuaciones), pero hay pérdidas debidas al redondeo y en ocasiones, al comparar el radio con las distancias de los puntos al centro de la circunferencia,  no se consideran iguales (usando el operador ==).
A veces las diferencias son insignificantes y los números no se consideran iguales y otras veces las diferencias son de la misma magnitud ¡y si se consideran iguales!.

Si sólo quisiera dibujar el círculo no importaría, el problema es que pienso usar el algoritmo para generar una triangulación de Deloné (o Delaunay) y si no hay seguridad en las comparaciones el sistema se va a complicar mucho.

En GMS el problema se puede solucionar con la función math_set_delta, sin embargo, en GM8 no existe esa función. He pensado en usar string_format para redondear hasta un número fijo de cifras decimales, pero esa función es demasiado lenta y sería necesario usarla en cientos o miles de iteraciones, además hay que hacer comparaciones de intervalos (<, >), entonces se debería usar la función real que también es lenta. Otra solución sería comparar los números a nivel de bits, pero no estoy seguro de cómo hacerlo. ¿Alguien tiene otra idea?

El proyecto está adjunto. Los controles son:
Click izquierdo - agregar un punto
Click derecho - reiniciar
Enter - agregar puntos con el teclado
Espacio - agregar puntos aleatorios

Las siguientes combinaciones de puntos producen resultados diferentes:

una causa error
( 365, 302 )
( 383, 184 )
( 548, 199 )

y la otra no
( 394, 470 )
( 369, 184 )
( 191, 251 )
« última modificación: Agosto 11, 2015, 06:25:32 pm por Clamud »

 


No Tienes Permisos Para Dar Puntos
point 0 Puntos

Este tema no recibió puntos.


Desconectado fasst007

  • Habitual
  • *
  • Puntos: 137
  • Mensajes: 96
    • Ver Perfil
Respuesta #1 en: Agosto 11, 2015, 04:48:58 am
Tengo dos alternativas, la más rápida sería la segunda pero de todas formas las listo a las dos:

PRIMERA ALTERNATIVA

¿la función abs es lenta? porque podrías comparar de la siguiente manera:

en lugar de: if (a == b) podrías hacer:

Primero definir una tolerancia o epsilon, como por ejemplo:0.0001;
 if( abs(a-b) < 0.0001)

esto sería como decir: la diferencia entre esos dos valores numéricos es menor a 0.0001, si lo es entra en el if considerándolos iguales.

SEGUNDA ALTERNATIVA

sino tambien sin usar abs:

diferencia = a-b;
if (diferencia < 0) diferencia *= -1;

if (diferencia < 0.0001)
{

}

o incluso mejor, ahorrando un cálculo de multiplicación y una escritura de menos en la variable diferencia:

if ((a-b) < 0) 
    diferencia = b-a;
else
    diferencia = a-b;

if (diferencia < 0.0001)
{

}


lo que no llamaría a ninguna función. Esto sería muy rápido.

Y como última alternativa quedaría que luego de diferencia = a-b; quitarle el bit de signo a la variable diferencia mediante alguna operación de bit (primero hay que saber la estructura interna de las variables numéricas de game maker) antes de hacer la comparacion . Pero creo que sería innecesario complicarse, el último código dado sería muy rápido.
« última modificación: Agosto 11, 2015, 05:19:26 am por fasst007 »

 


Desconectado fasst007

  • Habitual
  • *
  • Puntos: 137
  • Mensajes: 96
    • Ver Perfil
Respuesta #2 en: Agosto 11, 2015, 06:59:51 am
Te adjunté un archivo fuente que puedes descargar. Con la idea del comentario anterior hice un pequeño script llamado  "igual" que verifica si dos valores pasados por parámetros son iguales y devuelve "true" si lo son y "false" si no lo son. Aquí su código:

Código: [Seleccionar]
epsilon = 0.0001;

if((argument0-argument1) < 0)
   return((argument1-argument0) < epsilon);
else
   return((argument0-argument1) < epsilon);
   
y en lugar de:

Código: [Seleccionar]
if( r!=r1 or r!=r2 or r!=r3 ) show_message("radio incorrecto"); 

(en el script scCirc) lo reemplazo por su equivalente pero con la función nuestra:

   
Código: [Seleccionar]
if !(igual(r,r1) and igual(r,r2) and igual(r,r3)) show_message("radio incorrecto");
y parece que funciona.


« última modificación: Agosto 11, 2015, 07:16:45 am por fasst007 »

 


Desconectado Clamud

Respuesta #3 en: Agosto 11, 2015, 01:06:17 pm
Gracias fasst007, es un buen método. Entre todas las alternativas prefiero usar la que lleva abs, pues he notado que es mejor usar poco código; en GM8 la velocidad de interpretación del código es lenta en comparación con la velocidad de las funciones matemáticas.

Acabo de recordar que hay otro método para resolver el problema (con mediatrices), si en ese método se necesitan menos operaciones es probable que el resultado sea más preciso.

Y en mi mensaje anterior me equivoqué en el nombre de una función, en realidad se llama math_set_epsilon.

 


Desconectado fasst007

  • Habitual
  • *
  • Puntos: 137
  • Mensajes: 96
    • Ver Perfil
Respuesta #4 en: Agosto 11, 2015, 02:50:04 pm
Buen dato! entonces mucho mejor, porque el uso de abs() te simplifica mucho las cosas y te ahorrarías de hacer código casero. Claro yo estoy acostumbrado a un lenguaje compilado donde en ese caso sería mucho más rápido dos o tres if que un llamado a función y en game maker solo tengo un mes de experiencia XD y olvidaba que es interpretado.
« última modificación: Agosto 11, 2015, 03:46:36 pm por fasst007 »