Foros del Web » Programando para Internet » Javascript »

Dibujar óptimamente (Librería gráfica óptima)

Estas en el tema de Dibujar óptimamente (Librería gráfica óptima) en el foro de Javascript en Foros del Web. Hola foro. Recordando viejos tiempos he estado buscando un post que se llamaba "librería gráfica" o algo por el estilo en el que varios foreros ...
  #1 (permalink)  
Antiguo 04/01/2007, 18:20
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
Exclamación Dibujar óptimamente (Librería gráfica óptima)

Hola foro.

Recordando viejos tiempos he estado buscando un post que se llamaba "librería gráfica" o algo por el estilo en el que varios foreros publicamos ciertas soluciones para pintar gracias a javascript circunferencias, círculos, rectángulos, líneas y un largo etcétera de formas, y además "al vuelo". No he sido capaz de encontrarlo. ¿Se ha hecho limpieza de posts? Hace mucho que no paso por aquí...


El otro día me encontré con Real-Time 3D in Javascript, un render de una forma tridimensional en tiempo real con javascript (así, apurando un poco el inglés )

Bueno, después de digerirlo un rato -porque ya es bastante impresionante de por sí- me puse a husmearle un poco el código. Bien, como era poco legible y no tenía ganas de ponerme a descifrar miles de arrays me fije en la explicación que comenta la página en cuestión.


Pues eso, ya lo habréis leido, que con el border de una capa y su height 0px podemos obtener un triangulo rectángulo y patatín patatín...



Y de esta manera conseguimos un triangulo cualquiera.




Bueno pues he estado trasteando para hacer yo mismo mis propios triángulos según esta proposición y sí que lo he conseguido (con un poco de esfuerzo).

Lo que quiero plasmar aquí es la idea, y si alguien se atreve a desmenuzar el código o hacer un ejemplo de cómo demonios el creador de esta "estrella" es capaz de iluminar todo el conjunto y de moverla en tiempo real, que sepa que es bien recibido todo lo que postee aquí.


Bueno el código que escribí para rellenar de color tres puntos es este:
(en respuesta a este post)

Está algo comentado, pero creo que sólo lo voy a entender yo . Lo explicaré un poco:

-La función rect_triangle dibuja los 4 tipos de triangulos rectángulos que podemos conseguir gracias a los bordes de un DIV.
-La función rellenar es algo más compleja, hay que desmenuzarla. Además es recursiva, para liar un poco más :
He necesitado algo de papel y dibujar mucho para sacarla . Lo primero que mira es a ver si merece la pena dibujar el triángulo, si es suficientemente pequeño para detener la recursividad.
Después analizamos los lados del triangulo: si hay horizontales o verticales.

CASO1: Si tenemos de los dos tipos de línea en el triangulo existe un ángulo recto, luego lo rellenaremos directamente con rect_triangle.

CASO2, CASO3: Si hay una vertical o una horizontal trazaremos una línea horizontal o vertical (lo contrario de cada caso) por el punto intermedio horizontal o verticalmente para obtener dos sub-triangulos. Esos dos triángulos los rellenaremos recursivamente con rellenar.

CASO4: Si no hay ni verticales ni horizontales podemos trazar una vertical o una horizontal para dividir el triangulo en dos sub-triángulos como antes. Yo he escogido una horizontal por escoger alguna. Los dos triángulos que resulten también, recursivamente, con rellenar.
-Y las demás funciones son para apoyarse en ellas para hacer cosas más grandes. Algunas las he copiado, como i_r_r o las de trabajo con arrays, porque no quería comerme mucho la cabeza.



Bueno, animarlos en tiempo real ya sería un poco más complicado. Y de triángulos de cualquier índole hasta polígonos y demás formas sólo hay un paso, a ver quién será el valiente.



Nada más que deciros, espero que disfrutéis con el inventejo este de los tringulitos.


Un saludo a todos
__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.

Última edición por derkenuke; 04/01/2007 a las 18:26
  #2 (permalink)  
Antiguo 04/01/2007, 18:23
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
Re: Dibujar óptimamente (Librería gráfica óptima)

Inicio: (parte 1/2)

Código PHP:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<
html>
<
head>
  <
title>Triangulitos optimos</title>

<
style type="text/css">
/* <![CDATA[ */

div.triangulo {
    
position:absolute;
    
width0px;
    
height0px;
    
border-topsolid 20px transparent;
    
border-rightsolid 20px transparent;
    
border-leftsolid 20px transparent;
    
border-bottomsolid 20px transparent;
}

div.pto {
    
position:absolute;
    
width5pxheight5pxfont-size:1pxtop:0left:0;
}
/* ]]> */
</style>



<
script>
    var 
trans="transparent";
</script>

<!--[if lt IE 7]>
    <style type="text/css">
    /* <![CDATA[ */
    /* Hack for transparent borders in IE6 */
    div.triangulo {
        filter: chroma(color=cyan);
        overflow: hidden;
        font-size: 0;
        border-color: cyan;
    }
    /* ]]> */
    </style>
    <script type="text/javascript">
        /* <![CDATA[ */
        var trans = "cyan";
        /* ]]> */
    </script>
<![endif]-->

</head>

<body>

<script>

// eliminar los elementos duplicados de un array (se necesitara para la interseccion de arrays)
Array.prototype.removeDuplicates=function(compareFunction){ 
   if (!compareFunction) compareFunction=null;
   var a1=this.concat().sort(compareFunction);
   if (compareFunction){ 
      for (var i=0;i<a1.length;i++){ 
         var src=a1[i];
         for (var j=i+1;j<a1.length&&compareFunction(a1[j],src)==0;j++){} 
         if (j-1>i) a1.splice(i+1,j-i-1);
      } 
   }else{ 
      for (var i=0;i<a1.length;i++){ 
         var src=a1[i];
         for (var j=i+1;j<a1.length&&a1[j]==src;j++){} 
         if (j-1>i) a1.splice(i+1,j-i-1);
      } 
   } 
   return a1;


//interseccion entre dos arrays - devuelve un array con los elementos comunes de los dos arrays
Array.prototype.intersect=function(a2,compareFunction){ 
   if (!compareFunction) compareFunction=null;
   var a1=this.removeDuplicates(compareFunction);
   if (!a2) return a1;
   var a2=a2.removeDuplicates(compareFunction);
   var len2=a2.length;
   if (len2<a1.length){ 
      var c=a2; a2=a1; a1=c; c=null;
      len2=a2.length;
   } 
   if (compareFunction){ 
      for (var i=0;i<a1.length;i++){ 
         var src=a1[i],found=false,src;
         for (var j=0;j<len2&&compareFunction(src2=a2[j],src)!=1;j++) if (compareFunction(src,src2)==0) { found=true; break; } 
         if (!found) a1.splice(i--,1);
      } 
   }else{ 
      for (var i=0;i<a1.length;i++){ 
         var src=a1[i],found=false,src;
         for (var j=0;(j<len2)&&(src>=(src2=a2[j]));j++) if (src2==src) { found=true; break; } 
         if (!found) a1.splice(i--,1);
      } 
   } 
   return a1;
}

function compararV(a,b) {
    if(a.x==b.x && a.y==b.y) return 0;
    else if(a.x<b.x) return 1;
    else return -1;
}

/*        indice de los triangulos rectangulos
    90 ab dcha -> 1
    90 ab izq -> 2
    90 arr dcha -> 3
    90 arr izq -> 4
*/

//b es el tipo de triangulo; x,y su posicion; w,h sus dimensiones y col su color
function rect_triangle(b,x,y,w,h,col) {
    var div=document.createElement("DIV");
    div.className="triangulo";
    div.style.left=x+"px"; div.style.top=y+"px";
    //Todos nulos, luego tocaremos segun sea el conveniente (segun b) el borde que queramos;
    div.style.borderTop="solid 0px "+trans;
    div.style.borderRight="solid 0px "+trans;
    div.style.borderLeft="solid 0px "+trans;
    div.style.borderBottom="solid 0px "+trans;
    switch(b) {
        case 1:
            div.style.borderRight="solid "+w+"px "+col;
            div.style.borderTop="solid "+h+"px "+trans;
        break;
        case 2:
            div.style.borderLeft="solid "+w+"px "+col;
            div.style.borderTop="solid "+h+"px "+trans;
        break;
        case 3:
            div.style.borderRight="solid "+w+"px "+col;
            div.style.borderBottom="solid "+h+"px "+trans;
        break;
        default:
            div.style.borderLeft="solid "+w+"px "+col;
            div.style.borderBottom="solid "+h+"px "+trans;
        break;
    }
    document.body.appendChild(div);    
}

//dibuja un pequeño punto NO centrado en la posicion x,y
function pto(x,y,col) {
    if(col==null) col="red";
    var div=document.createElement("DIV");
    div.className="pto";
    div.style.left=x+"px"; div.style.top=y+"px";
    div.style.backgroundColor=col;
    div.style.zIndex="10";
    document.body.appendChild(div);    
}


/**********************************************************************/

//vertice - para crear objetos que tengan como propiedades x,y
function v(x,y) {
    var a=new Array(); a["x"]=x; a["y"]=y; return a;
}

//Interseccion de rectas - las formadas por v1-v2 y v3-v4. Devuelve un v.
function i_r_r(v1,v2,v3,v4){
    var lineAx1=v1.x, lineAy1=v1.y, lineAx2=v2.x, lineAy2=v2.y, lineBx1=v3.x, lineBy1=v3.y, lineBx2=v4.x, lineBy2=v4.y;
    var aM, bM, aB, bB, isX=0, isY=0;
    if((lineAx2-lineAx1)==0){
        isX=lineAx1;
        bM=(lineBy2-lineBy1)/(lineBx2-lineBx1);
        bB=lineBy2-bM*lineBx2;
        isY=bM*isX+bB;
    }
    else if((lineBx2-lineBx1)==0){
        isX=lineBx1;
        aM=(lineAy2-lineAy1)/(lineAx2-lineAx1);
        aB=lineAy2-aM*lineAx2;
        isY=aM*isX+aB;
    }
    else{
        aM=(lineAy2-lineAy1)/(lineAx2-lineAx1);
        bM=(lineBy2-lineBy1)/(lineBx2-lineBx1);
        aB=lineAy2-aM*lineAx2;
        bB=lineBy2-bM*lineBx2;
        isX=Math.max(((bB-aB)/(aM-bM)),0);
        isY=aM*isX+aB;
    }
    return v(isX,isY);

__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.
  #3 (permalink)  
Antiguo 04/01/2007, 18:24
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
Re: Dibujar óptimamente (Librería gráfica óptima)

Fin: parte (2/2)

Código PHP:
//rellena tres puntos del plano de un color
function rellenar(v1,v2,v3,col) {
    
//dibujamos los vertices como puntos
        //pto(v1.x,v1.y,"pink"); pto(v2.x,v2.y,"pink"); pto(v3.x,v3.y,"pink");
    //alert("INICIO: "+v1.x+","+v1.y+"; "+v2.x+","+v2.y+"; "+v3.x+","+v3.y);
    //variables de dimension que necesitaremos
        
var minX,minY,maxX,maxY;
        
minX=Math.min(v1.x,v2.x,v3.x); maxX=Math.max(v1.x,v2.x,v3.x);
        
minY=Math.min(v1.y,v2.y,v3.y); maxY=Math.max(v1.y,v2.y,v3.y);
        var 
ancho=maxX-minX; var alto=maxY-minY;
    
//triangulo demasiado pequeño como para esforzarse en dibujarlo (ademas fin de la recursividad)
    
if( ancho*alto/) return true;
    
//miramos a ver si existen lados horizontales y verticales, y entre que vertices
        
var hayVert=false;
        if( 
v1.x==v2.xhayVert="v1,v2";
        else if(
v1.x==v3.xhayVert="v1,v3";
        else if(
v2.x==v3.xhayVert="v2,v3";
        var 
hayHoriz=false;
        if( 
v1.y==v2.yhayHoriz="v1,v2";
        else if(
v1.y==v3.yhayHoriz="v1,v3";
        else if(
v2.y==v3.yhayHoriz="v2,v3";
    if(
hayHoriz && hayVert) {        //hay horizontales y verticales a la vez --> es angulo recto
        //averiguar el tipo - cual tiene el ang recto? cual esta en hayHoriz y en hayVert a la vez?
            //donde esta el angulo de 90?
                
var hayHorizHayVert=(hayHoriz+hayVert);
                if( 
hayHorizHayVert.split("v1").length == ) var noventa=v1;
                else if( 
hayHorizHayVert.split("v2").length == ) var noventa=v2;
                else if( 
hayHorizHayVert.split("v3").length == ) var noventa=v3;
            
// ahora hay que saber si noventa es el mas alto o el mas a la izq
                
if(minX==noventa.&& minY==noventa.y)    var tipo=4;
                else if(
maxX==noventa.&& maxY==noventa.y) var tipo=1;
                else if(
minX==noventa.&& maxY==noventa.y) var tipo=2;
                else if(
maxX==noventa.&& minY==noventa.y) var tipo=3;
        return 
rect_triangle(tipo,minX,minY,ancho,alto,col);
    }
    else if(!
hayHoriz && !hayVert) {        //si no tiene ninguna forma en concreto
        //tiramos una horiz por el punto intermedio segun las Y
        
if((minY==v1.&& maxY==v3.y) || (maxY==v1.&& minY==v3.y)) { var intermedioY=v2; var noIntermedioY=[v1,v3]; }
        else if((
minY==v2.&& maxY==v3.y) || (maxY==v2.&& minY==v3.y)) { var intermedioY=v1; var noIntermedioY=[v2,v3]; }
        else if((
minY==v1.&& maxY==v2.y) || (maxY==v1.&& minY==v2.y)) { var intermedioY=v3; var noIntermedioY=[v1,v2]; }
        if((
minX==v1.&& maxX==v3.x) || (maxX==v1.&& minX==v3.x)) { var intermedioX=v2; var noIntermedioX=[v1,v3]; }
        else if((
minX==v2.&& maxX==v3.x) || (maxX==v2.&& minX==v3.x)) { var intermedioX=v1; var noIntermedioX=[v2,v3]; }
        else if((
minX==v1.&& maxX==v2.x) || (maxX==v1.&& minX==v2.x)) { var intermedioX=v3; var noIntermedioX=[v1,v2]; }
        var 
noIntermedio=noIntermedioY.intersect(noIntermedioXcompararV);
        
//alert(noIntermedioY[0].x+","+noIntermedioY[0].y+"; "+noIntermedioY[1].x+","+noIntermedioY[1].y+"\n"+noIntermedioX[0].x+","+noIntermedioX[0].y+"; "+noIntermedioX[1].x+","+noIntermedioX[1].y);
        //alert(noIntermedio[0].x+","+noIntermedio[0].y);
        
var nuevoV=v(0,intermedioY.y);
        
//interseccion de las rectas horizontal por intermedioY y el lado opuesto del triangulo (los q no son intermedioY)
        
nuevoV.i_r_r(intermedioYv(12457.78,intermedioY.y) ,noIntermedioY[0],noIntermedioY[1]).x;
        
//alert(nuevoV.x);
        //pto(nuevoV.x,nuevoV.y,"red");
        
if(noIntermedio.length==2) {        // intermedioX==intermedioY, luego hay dos noIntermedios
            
rellenar(intermedioY,nuevoV,noIntermedio[0],col);
            
rellenar(intermedioY,nuevoV,noIntermedio[1],col);
        }
        else {
            
rellenar(intermedioY,nuevoV,noIntermedio[0],col);
            
rellenar(intermedioY,nuevoV,intermedioX,col);
        }
    }
    else if(
hayHoriz) {
        
//hallamos el intermedio en X
            
if((minX==v1.&& maxX==v3.x) || (maxX==v1.&& minX==v3.x)) { var intermedioX=v2; var noIntermedioX=[v1,v3]; }
            else if((
minX==v2.&& maxX==v3.x) || (maxX==v2.&& minX==v3.x)) { var intermedioX=v1; var noIntermedioX=[v2,v3]; }
            else if((
minX==v1.&& maxX==v2.x) || (maxX==v1.&& minX==v2.x)) { var intermedioX=v3; var noIntermedioX=[v1,v2]; }
        
//una vertical por ahi cortara a un lado del triangulo en un punto, qdaran dos triangulos divididos
            
var nuevoV=v(intermedioX.x,0);
            
nuevoV.y=i_r_rintermedioXv(intermedioX.x,12.125), noIntermedioX[0], noIntermedioX[1] ).y;
        
//rellenamos triangulo parcial 1
            
rellenar(intermedioX,nuevoV,noIntermedioX[0],col);
        
//rellenamos triangulo parcial 2
            
rellenar(intermedioX,nuevoV,noIntermedioX[1],col);
    }
    else if(
hayVert) {
        
//hallamos el intermedio en Y
        
if((minY==v1.&& maxY==v3.y) || (maxY==v1.&& minY==v3.y)) { var intermedioY=v2; var noIntermedioY=[v1,v3]; }
        else if((
minY==v2.&& maxY==v3.y) || (maxY==v2.&& minY==v3.y)) { var intermedioY=v1; var noIntermedioY=[v2,v3]; }
        else if((
minY==v1.&& maxY==v2.y) || (maxY==v1.&& minY==v2.y)) { var intermedioY=v3; var noIntermedioY=[v1,v2]; }
        
//una horizontal por ahi cortara a un lado del triangulo en un punto
            
var nuevoV=v(0,intermedioY.y);
            
nuevoV.x=i_r_rintermedioYv(22.567,intermedioY.y), noIntermedioY[0], noIntermedioY[1] ).x;
            
//pto(nuevoV.x,nuevoV.y,"red");
        //rellenamos triangulo parcial 1
            
rellenar(intermedioY,nuevoV,noIntermedioY[0],col);
        
//rellenamos triangulo parcial 2
            
rellenar(intermedioY,nuevoV,noIntermedioY[1],col);
    }
}


var 
p1=v(100,200); pto(p1.x,p1.y);
var 
p2=v(300,100); pto(p2.x,p2.y);
var 
p3=v(400,400); pto(p3.x,p3.y);
rellenar(p1,p2,p3"orange");

var 
v1=v(600,200); pto(v1.x,v1.y);
var 
v2=v(800,100); pto(v2.x,v2.y);
var 
v3=v(650,90); pto(v3.x,v3.y);
rellenar(v1,v2,v3"blue");


</script>

</body>

</html> 
__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.
  #4 (permalink)  
Antiguo 05/01/2007, 02:13
Avatar de caricatos
Moderador
 
Fecha de Ingreso: abril-2002
Ubicación: Torremolinos (Málaga)
Mensajes: 19.607
Antigüedad: 22 años, 1 mes
Puntos: 1284
Re: Dibujar óptimamente (Librería gráfica óptima)

Hola derkenuke, hacía tiempo que no se te veía... Felicidades :

Sobre una librería gráfica, he puesto un tema hace tiempo: Librería gráfica

Aunque recuerdo que tu habías empezado un tema similar, pero no lo he encontrado.

La verdad es que es interesante la propuesta, pero el código que he visto me pareció muy complejo.

Saludos
__________________
Por favor:
No hagan preguntas de temas de foros en mensajes privados... no las respondo
  #5 (permalink)  
Antiguo 05/01/2007, 21:36
Avatar de derkenuke
Colaborador
 
Fecha de Ingreso: octubre-2003
Ubicación: self.location.href
Mensajes: 2.665
Antigüedad: 20 años, 6 meses
Puntos: 45
Re: Dibujar óptimamente (Librería gráfica óptima)

Sí, al final me salió un código mucho más complejo de lo que pensaba en un principio.

Es que el principal problema radica en dividir un triangulo cualquiera (el de la imagen posteada por ejemplo) en triángulos rectángulos, y sólo se me ocurrió esa idea de dividirlo a su vez con líneas horizontales y verticales. Quizás haya otra manera más optima o algún algoritmo.. pero si existe no he dado con ello.

Es complicado también identificar el punto intermedio, más de lo que parece. Quizás debería haber hecho funciones únicamente para ello.. así quedaría más legible. Son esos condicionales:
Código PHP:
        //hallamos el intermedio en X
            
if((minX==v1.&& maxX==v3.x) || (maxX==v1.&& minX==v3.x)) { var intermedioX=v2; var noIntermedioX=[v1,v3]; }
            else if((
minX==v2.&& maxX==v3.x) || (maxX==v2.&& minX==v3.x)) { var intermedioX=v1; var noIntermedioX=[v2,v3]; }
            else if((
minX==v1.&& maxX==v2.x) || (maxX==v1.&& minX==v2.x)) { var intermedioX=v3; var noIntermedioX=[v1,v2]; } 
De todas maneras si se mira con detenimiento la función rellenar no tiene más que cuatro cositas, y creo que me ha salido bastante fiable. Lo único es que creo que surgen anomalías cuando los puntos están casi alineados... pero de momento sirve.

Las demás funciones creo que son totalmente fiables y rápidas, no hay nada que temer


Los resultados en IE y en Firefox difieren un poco. En IE se ven algunos huecos. Voy a postear algunas imagenes con resultados:

Triángulos en Firefox:

Triángulos en IE: (ojo en la cercanía de los vértices, como aparecen las divisiones)

Un entorno 3D con su cielo y su iluminación en Firefox: (bastante bueno aunque se aprecia alguna diagonal)

El mismo entorno en IE (se ven muchas mas líneas que dividen a los triángulos)



He tenido que implementar la función cuadrilatero:
Código PHP:
function cuadrilatero(v1,v2,v3,v4,col) {
    
rellenar(v1,v2,v3,col);
    
rellenar(v3,v4,v1,col);

que es la solemne bobada de dividir un cuadrilatero en dos triángulos. Sé que no funciona en todos los cuadriláteros, habría que hacer comprobaciones para rellenar lo que hay que rellenar y no otra zona, pero bueno, de momento me ha servido.


Bueno espero que os guste. A mi me ha satisfecho bastante la perspectiva esa jeje. Una variable contador me dice que hay entre 70 y 90 triángulos rectángulos (divs) en esa captura (varía porque la sombra es aleatoria, puede salir más alta o más baja, se ve en las dos imágenes). Desde luego son muy pocos comparado con los cientos que necesitaríamos con un sólo triángulo si lo rellenasemos con horizontales.


Un saludo a todos, y gracias por responder caricatos
__________________
- Haz preguntas inteligentes, y obtendrás más y mejores respuestas.
- Antes de postearlo Inténtalo y Búscalo.
- Escribe correctamente tus mensajes.
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 19:08.