Foro de elhacker.net

Programación => Programación General => Mensaje iniciado por: WHK en 2 Julio 2013, 18:18 pm



Título: Dilema matemático con porcentajes
Publicado por: WHK en 2 Julio 2013, 18:18 pm
Hola, estoy programando un editor de imagenes pero me he detenido en un pequeño pero gran dilema.

Lo que hago actualmente es tener una imagen escalada y rotada y a partir de esto necesito mover algunos objetos que estan dentro de el calculando la posición del mouse.

Para no complicar mas el tema iré directo al grano.

Necesito sacar este calculo:
Tengo zoomActual = x%.
Tengo zoomCero = 100%.
El nuevo zoom debe ser el inverso doble, por ejemplo:

Si el zoom actual es 100% entonces el nuevo zoom debe ser 100
Si el zoom actual es 75% entonces el nuevo zoom debe ser 150
Si el zoom actual es 50% entonces el nuevo zoom debe ser 200
Si el zoom actual es 25% entonces el nuevo zoom debe ser 400
Si el zoom actual es 12.5% entonces el nuevo zoom debe ser 800
...

Como ven, no es un zoom inverso normal porque los valores estan relacionados de una manera especial que no logro comprender por completo, solo se que he llegado hasta este punto programando el editor desde cero y he llegado a la conclusión de que para que funcione el movimiento de algunos objetos dentro de la imagen escalada sobre este zoom debo realizar exactamente este calculo (en pixeles).

Un % invertido simple lo calculé de esta manera:
Código:
var zoomInvertido = (-(data.ob.zoomActual - 100) + 100);

De esta manera si el zoom actual es 50% entonces el nuevo es 150%, se invierte bién pero necesito el doble, no la mitad mas el normal.

Si el zoom es la mitad del tamaño entonces necesito aplicar el doble, bajo este concepto necesito calcular los escalados de objetos con sus movimientos.

No se ve tan complejo asi que debe tener un nombre para esta ecuación, el problema es que no se como se llama porque es primera ves que lo veo y llegué hasta ahí, por ende no se como buscarlo en google tampoco.

----------------------------------
Para los que quiren saber que estoy haciendo exactamente pueden continuar leyendo, esto no es necesario para saber como realizar el calculo.

Estoy programando en javascript, HTML5 y CSS3, tengo unn editor de imagenes, este debe tener multiples selecciones en forma de rectangulos, cuando la imagen es rotada entonces los selectores tambien lo hacen sin perder las posiciones, cuando escalo la imagen entonces los selectores tambien se escalan, pero no estoy utilizando redimensionado de ancho y alto sino la propiedad directa de css transform scale y transform rotate para poder darle algunas animaciones extras sin perder la fluidez.

Intenté hacer esto con jquery UI draggable, el problema es que jQuery tiene un bug: no soporta css3 por lo tanto las posiciones absolutas no son reales al momento de escalar o rotar un objeto desde css3 haciendo que el arrastre del objeto se pierda en el infinito, por ende comencé a hacerlo de forma manual utilizando css top y left teniendo como referencia el movimiento del mouse calculando desde la posicion inicial desde que comenzó el click hasta que se suelta el botón.... esa misma cantidad de pixeles recorridos se aplica a la división del selector, entonces cuando se hace zoom (escalado) de 50% es necesario recorrer la misma cantidad de pixeles multiplicado por el doble, o sea un 200% de esa misma cantidad.

Por ejemplo, digamos que la imagen tiene 1000px de ancho, el mouse recorre una distancia de 50px, entonces el selector debe recorrer esa misma distancia recorriendo desde el pixel 0 hasta el pixel 50.


Cuando la imagen se escala a un 50% entonces la imagen queda en 500px (de manera virtual porque desde el DOM sigue teniendo 1000px), el cuadro de recorte también es escalado ya que lo que se está escalando es una division que contiene esta imagen mas los selectores.

Cuando el mouse recorre 50px cuanto debe recorrer la division? ya no son 50px porque quedará mas atrás del mouse, recordemos que con css el 50% de un ancho se convierte en 1:2, o sea dentro de un espacio de 500px del document hay 1000px, por lo tanto la división recorrió solo el 50% de los pixeles totales en ves del 100%, o sea 25px.

Para solucionar esto debo sumarle la cantidad de pixeles según el zoom actual, por eso:

Si el zoom actual es 100% entonces debe recorrer el 100% de los pixeles.
Si el zoom actual es 75% entonces debe recorrer el 150% de los pixeles.
Si el zoom actual es 50% entonces debe recorrer el 200% de los pixeles.

Recordemos que al hacerle un zoom de 50% ha quedado en la mitad del tamaño, por lo tanto los pixeles recorridos deben aumentar al doble para contrarrestar este desface.

Intenté realizar un zoom invertido simple, o sea si el zoom es de 50% entonces que recorra el 150% de pixeles totales pero no funcionó, mientra mas pequeña la imagen había mas desface, la division se iba quedando atrás, pensé que por lógica debería ser el 200% para contrarrestar la mitad con un doble asi que lo probé y funcionó de lujo.

Lamentablemente tampoco puedo utilizar $(foo).offset().[top, left] porque en javascript eso no existe, jquery lo que hace es utilizar offsetParent para calcular la posición de cada objeto pariente pero no realiza correctamente los calculos si este está rotado o tiene algún zoom ya que aun no está preparado para CSS3. Ya está reportado hace casi 2 años atrás, algunos dicen que es un bug del navegador, otros dicen que es problema del core, otros dicen que es problema del UI, etc, hasta ahora nadie lo ha solucionado, por eso tube que hacerlo de forma manual.

Se comprende la idea?
-------------------------------------------

Alguien sabe como realizar este calculo de manera matematica? tengo un javascript con mas de 800 lineas con calculos complejos matematicos y me siento un poco cansado mentalmente, cada ves me cuesta mas realizar un calculo a pesar de que pueda ser mas lógico.

Gracias.

PD: por si alguien quiere darle un vistazo al código:
Código
  1. iniciaArrastreCorte: function(obMover){
  2. /* La división padre puede estar escalada, por lo cual la cantidad
  3. * de pixeles a mover ya no son los mismos. Se ajustará al % del
  4. * escalado v/s la posición real del mouse dependiendo de la dirección de
  5. * la rotación. No se puede mover directamente calculando la posición del mouse
  6. * debido a que el escalado y la rotación se realiza via CSS, las propiedades
  7. * top y left no varían después de realizar esta modificación ya que por estandar
  8. * no se actualiza el DOM, las propiedades offset.left y offset.top tampoco son
  9. * reales ya que se relativizan según el tamaño original no rotado.
  10. * Por ende la única manera de calcular estos tamaños es creando multiples wraps
  11. * realizando calculos inversos sobre las redimensiones y posiciones relativas para
  12. * luego calcular los reales y volver a convertir. */
  13.  
  14. /* Matris de datos para el cálculo del arrastre del objeto */
  15. var data = {
  16. ob : this,
  17. mouse : {
  18. inicio : { left: 0, top: 0 },
  19. final : { left: 0, top: 0 }
  20. },
  21. obMover : {
  22. inicio : { top: 0, left: 0 },
  23. final : { top: 0, left: 0 },
  24. dimensiones : { width: 0, height: 0 }
  25. },
  26. imagenPrincipal : { width: 0, height: 0 },
  27. enganchado : false
  28. };
  29.  
  30. /* Evento del movimiento del mouse */
  31. $(document).mousemove(function(e){
  32.  
  33. /* Captura inicial de posiciones y propiedades */
  34. if(data.enganchado == false){
  35.  
  36. /* Posición actual del mouse */
  37. data.mouse.inicio.left = e.pageX;
  38. data.mouse.inicio.top = e.pageY;
  39. data.mouse.final.left = e.pageX;
  40. data.mouse.final.top = e.pageY;
  41.  
  42. /* Dimensiones de la imagen principal */
  43. data.imagenPrincipal.width = $('._cargar_imagen_original').width();
  44. data.imagenPrincipal.height = $('._cargar_imagen_original').height();
  45.  
  46. /* Posición inicial del corte */
  47. data.obMover.inicio.left = parseFloat(obMover.css('left'));
  48. data.obMover.inicio.top = parseFloat(obMover.css('top'));
  49.  
  50. /* Dimensiones del corte */
  51. data.obMover.dimensiones.width = obMover.width();
  52. data.obMover.dimensiones.height = obMover.height();
  53.  
  54. data.enganchado = true;
  55.  
  56. /* Después de obtener las posiciones iniciales comienza el movimiento del objeto */
  57. }else{
  58. data.mouse.final.left = e.pageX;
  59. data.mouse.final.top = e.pageY;
  60.  
  61. /* Dependiendo de la rotación actual es la posición del corte */
  62. if(parseInt(data.ob.rotacionActualReal) == 90){
  63. }else if(parseInt(data.ob.rotacionActualReal) == 180){
  64. }else if(parseInt(data.ob.rotacionActualReal) == 270){
  65. }else{ /* 0 Grados. */
  66.  
  67. // Mueve la division el mismo % que el % movido por el mouse (funciona, pero con problemas al establecer valores negativos para poder rotar)
  68. /* Dimensiones reales de la imagen */
  69. //espectro_P1 = ((imgW * ob.zoomActual) / 100);
  70. //espectro_P2 = ((imgH * ob.zoomActual) / 100);
  71.  
  72. /* % del recorrido del mouse */
  73. //espectro_P3 = (((e.pageY - inicioY) * 100) / espectro_P1);
  74. //espectro_P4 = (((e.pageX - inicioX) * 100) / espectro_P2);
  75.  
  76. /* pixeles que debe recorrer la división */
  77. //espectro_P5 = obY + ((imgW * espectro_P3) / 100);
  78. //espectro_P6 = obX + ((imgH * espectro_P4) / 100);
  79.  
  80. //espectro_P7 = espectro_P5; /* top */
  81. //espectro_P8 = espectro_P6; /* left */
  82.  
  83. //var descompuesto = -(data.ob.zoomActual - 100);
  84. // 50 debe ser 200
  85. // 25 debe ser 400
  86. // 12.5 debe ser 800
  87. //var compuesto = (descompuesto + 100);
  88. var zoomInvertido = (-(data.ob.zoomActual - 100) + 100); // invierte el zoom teniendo como centro único el 100%
  89.  
  90. console.log(data.ob.zoomActual + ' v/s ' + zoomInvertido);
  91. data.obMover.final.top = data.obMover.inicio.top + ((zoomInvertido * (data.mouse.final.top - data.mouse.inicio.top)) / 100);
  92. data.obMover.final.left = data.obMover.inicio.left + ((zoomInvertido * (data.mouse.final.left - data.mouse.inicio.left)) / 100);
  93.  
  94. }
  95.  
  96. /* El corte no puede salir de la imagen */
  97. if(data.obMover.final.top < 0)
  98. data.obMover.final.top = 0;
  99. if(data.obMover.final.left < 0)
  100. data.obMover.final.left = 0;
  101. if(data.obMover.final.top > (data.imagenPrincipal.height - data.obMover.dimensiones.height))
  102. data.obMover.final.top = (data.imagenPrincipal.height - data.obMover.dimensiones.height);
  103. if(data.obMover.final.left > (data.imagenPrincipal.width - data.obMover.dimensiones.width))
  104. data.obMover.final.left = (data.imagenPrincipal.width - data.obMover.dimensiones.width);
  105.  
  106. /* Finalmente establece las coordenadas exactas */
  107. obMover.css({
  108. top : parseFloat(data.obMover.final.top),
  109. left : parseFloat(data.obMover.final.left)
  110. });
  111. }
  112. });
  113. },

Eliminé varias lineas de esta función para que no se viera tan gigante, solo muestra la función cuando la rotación es cero.

Tengo varios bosquejos con diferentes maneras de calcular estas posiciones utilizando diferentes técnicas con css, jquery, etc pero quiero probar con esta.

No quiero que me solucionen el tema de la programación en si, por algo lo puse en el foro de programación general, solamente necesito el tema de como calcular la ecuación.


Título: Re: Dilema matemático con porcentajes
Publicado por: WHK en 2 Julio 2013, 20:31 pm
Bueno, lo he solucionado haciendolo de otra manera, aunque de todas maneras quedé intrigado sobre como realizar esta operación matemática, si alguien tiene el dato se lo agradecería un montón.

Después de un rico almuerzo como que me vino denuevo el ánimo de crear un nuevo cálculo basado en otro antiguo que tenía y solucionar el tema de las posiciones :D .

El código ha quedado de la siguiente manera (a mas de alguno le servirá):

Código
  1. iniciaArrastreCorte: function(obMover){
  2. /* La división padre puede estar escalada, por lo cual la cantidad
  3. * de pixeles a mover ya no son los mismos. Se ajustará al % del
  4. * escalado v/s la posición real del mouse dependiendo de la dirección de
  5. * la rotación. No se puede mover directamente calculando la posición del mouse
  6. * debido a que el escalado y la rotación se realiza via CSS, las propiedades
  7. * top y left no varían después de realizar esta modificación ya que por estandar
  8. * no se actualiza el DOM, las propiedades offset.left y offset.top tampoco son
  9. * reales ya que se relativizan según el tamaño original no rotado.
  10. * Por ende la única manera de calcular estos tamaños es creando multiples wraps
  11. * realizando calculos inversos sobre las redimensiones y posiciones relativas para
  12. * luego calcular los reales y volver a convertir. */
  13.  
  14. /* Matris de datos para el cálculo del arrastre del objeto */
  15. var data = {
  16. ob : this,
  17. mouse : {
  18. inicio : { left: 0, top: 0 },
  19. final : { left: 0, top: 0 }
  20. },
  21. obMover : {
  22. inicio : { top: 0, left: 0 },
  23. final : { top: 0, left: 0 },
  24. dimensiones : { width: 0, height: 0 }
  25. },
  26. imagenPrincipal : {
  27. real : { width: 0, height: 0 },
  28. virtual : { width: 0, height: 0 }
  29. },
  30. enganchado : false
  31. };
  32.  
  33. /* Evento del movimiento del mouse */
  34. $(document).mousemove(function(e){
  35.  
  36. /* Captura inicial de posiciones y propiedades */
  37. if(data.enganchado == false){
  38.  
  39. /* Posición actual del mouse */
  40. data.mouse.inicio.left = e.pageX;
  41. data.mouse.inicio.top = e.pageY;
  42. data.mouse.final.left = e.pageX;
  43. data.mouse.final.top = e.pageY;
  44.  
  45. /* Dimensiones de la imagen principal */
  46. data.imagenPrincipal.real.width = $('._cargar_imagen_original').width();
  47. data.imagenPrincipal.real.height = $('._cargar_imagen_original').height();
  48. data.imagenPrincipal.virtual.width = (($('._cargar_imagen_original').width() * data.ob.zoomActual) / 100);
  49. data.imagenPrincipal.virtual.height = (($('._cargar_imagen_original').height() * data.ob.zoomActual) / 100);
  50.  
  51. /* Posición inicial del corte */
  52. data.obMover.inicio.left = parseFloat(obMover.css('left'));
  53. data.obMover.inicio.top = parseFloat(obMover.css('top'));
  54.  
  55. /* Dimensiones del corte */
  56. data.obMover.dimensiones.width = obMover.width();
  57. data.obMover.dimensiones.height = obMover.height();
  58.  
  59. data.enganchado = true;
  60.  
  61. /* Después de obtener las posiciones iniciales comienza el movimiento del objeto */
  62. }else{
  63. data.mouse.final.left = e.pageX;
  64. data.mouse.final.top = e.pageY;
  65.  
  66. /* Dependiendo de la rotación actual es la posición del corte */
  67. if(parseInt(data.ob.rotacionActualReal) == 90){
  68. data.obMover.final.left = (data.obMover.inicio.left + ((data.imagenPrincipal.real.height * (((data.mouse.final.top - data.mouse.inicio.top) * 100) / data.imagenPrincipal.virtual.height)) / 100));
  69. data.obMover.final.top = (data.obMover.inicio.top + ((data.imagenPrincipal.real.width * ((-(data.mouse.final.left - data.mouse.inicio.left) * 100) / data.imagenPrincipal.virtual.width)) / 100));
  70.  
  71. }else if(parseInt(data.ob.rotacionActualReal) == 180){
  72. }else if(parseInt(data.ob.rotacionActualReal) == 270){
  73. }else{ /* 0 Grados. */
  74. data.obMover.final.left = (data.obMover.inicio.left + ((data.imagenPrincipal.real.width * (((data.mouse.final.left - data.mouse.inicio.left) * 100) / data.imagenPrincipal.virtual.width)) / 100));
  75. data.obMover.final.top = (data.obMover.inicio.top + ((data.imagenPrincipal.real.height * (((data.mouse.final.top - data.mouse.inicio.top) * 100) / data.imagenPrincipal.virtual.height)) / 100));
  76.  
  77. }
  78.  
  79. /* El corte no puede salir de la imagen */
  80. if(data.obMover.final.top < 0)
  81. data.obMover.final.top = 0;
  82. if(data.obMover.final.left < 0)
  83. data.obMover.final.left = 0;
  84. if(data.obMover.final.top > (data.imagenPrincipal.height - data.obMover.dimensiones.height))
  85. data.obMover.final.top = (data.imagenPrincipal.height - data.obMover.dimensiones.height);
  86. if(data.obMover.final.left > (data.imagenPrincipal.width - data.obMover.dimensiones.width))
  87. data.obMover.final.left = (data.imagenPrincipal.width - data.obMover.dimensiones.width);
  88.  
  89. /* Finalmente establece las coordenadas exactas */
  90. obMover.css({
  91. top : parseFloat(data.obMover.final.top),
  92. left : parseFloat(data.obMover.final.left)
  93. });
  94. }
  95. });
  96. },

Saludos.


Título: Re: Dilema matemático con porcentajes
Publicado por: 1mpuls0 en 2 Julio 2013, 20:57 pm
Es lo único que se me ocurre LOL, se puede simplificar.

ZN = 100 + ( ( 100 - ZA ) * 2 * ( 100 / ZA / 2 ) )

Dónde:
ZA = Zoom Actual
ZN = Zoom Nuevo

Por cierto, dudo que pase pero cuando ZA=0 a cuanto equivale ZN?

Saludos.




Título: Re: Dilema matemático con porcentajes
Publicado por: 0xDani en 2 Julio 2013, 21:04 pm
Tienes que dividir el zoom cuyo inverso quieres calcular entre 100, y multiplicar por la inversa del resultado.

De esta forma, el zoom inverso de 50:

Código:
50/100 = 1/2
Inversa de 1/2 = 2
100*2  = 200

Creo que es eso lo que quieres, comprueba que funcione.


Saludos.

PD: La inversa de un número es, por definición, aquel que multiplicado por el primero resulta en 1, esto es 1/x.


Título: Re: Dilema matemático con porcentajes
Publicado por: WHK en 2 Julio 2013, 21:09 pm
Es lo único que se me ocurre LOL, se puede simplificar.

ZN = 100 + ( ( 100 - ZA ) * 2 * ( 100 / ZA / 2 ) )

Dónde:
ZA = Zoom Actual
ZN = Zoom Nuevo

Por cierto, dudo que pase pero cuando ZA=0 a cuanto equivale ZN?

Saludos.




El rango minimo seleccionable es 1 y maximo es 200.

Gracias a los dos, me han servido de mucho los ejemplos :D


Título: Re: Dilema matemático con porcentajes
Publicado por: WHK en 4 Julio 2013, 00:03 am
Genial! ahora los tooltips embebidos en el escalado de la imagen mantienen su tamaño original :D

Código
  1. var invertido = 100 + ((100 - this.zoomActual) * 2 * (100 / this.zoomActual / 2));
  2. $('._cargar_imagen_original_corte_info').css({
  3. 'transform': 'scale(' + (invertido / 100) + ')'
  4. });