4 votos

Cómo calcular los ángulos y las coordenadas X,Y para dibujar una mano de cartas en un lienzo

Escenario: Estoy programando un módulo para dibujar una baraja de cartas en un lienzo como si fueran sostenidas por una persona.

Editar:

He limpiado la pregunta lo mejor que he podido para que sea más clara.

Lo que busco ahora :

Las coordenadas X,Y de cada estrella que representan el centro de cada carta, así como su rotación desde ese punto para formar un semicírculo.

Para que sea más sencillo que antes, ahora también se puede asumir:

  1. El ángulo total que cubren las tarjetas es 90º .
  2. Hay 5 tarjetas .
  3. La carta del medio siempre estará en X = 0
  4. Cada tarjeta final estará en una rotación de 30º (el lado izquierdo será -30º)
  5. El lienzo tiene un tamaño fijo. Nota: He puesto 200 como altura, aunque es un número algo arbitrario y ni siquiera sé si ayuda o no.

También debo mencionar que el dibujo no está exactamente a escala, lo hice lo mejor que pude con una herramienta primitiva para dibujar formas geométricas


Solución:

La fuente de mi solución se puede encontrar aquí: https://github.com/tcrosen/playing-cards

Puede encontrar una demostración en funcionamiento aquí: http://tcrosen.github.com/playing-cards/demo.html

function drawHand(el, originX, originY, numberOfCards, cardWidth, cardHeight, showOrigin) {

    // The angle off the origin that each card will be referenced from
    // The +1 is added because we want the first card to be positioned above the origin
    var angle = 180 / (numberOfCards + 1);

    // How far each card will be from the origin of the hand.
    // This is proportional to the size of the card so that larger cards avoid too much overlap
    var radius = cardWidth * 1.2;

    // Through trial & error I determined a small hand (3-5 cards) looks most realistic 
    // when the end cards are at a rotation of 30 degrees (90 - 5 * 12). However when larger hands are created
    // the end cards must be rotated at a larger angle in order to be "held" properly.  Anything that would
    // calculate to an angle > 30 (6 cards or more) is simply capped at 45 degrees.
    var endRotation = 12 * numberOfCards > 60 ? 45 : 90 - 12 * numberOfCards;

    // Find an equal angle to split the cards across the entire hand
    var rotationIncrement = endRotation * 2 / (numberOfCards + 1);

    // If the user wants to see the origin for debugging/design purposes, show an X there
    if (showOrigin) {
        $(el).append($('<span>X</span>').css('color', 'red').css('position', 'absolute').css('top', originY + 'px').css('left', originX + 'px'));
    }

    // Loop through each card
    // *Note: I start at 1 (instead of 0) in order to avoid multiplying by 0 and ending up with flat angles. 
    //  If you are using an array of cards (eventual scenario) you would need to account for the 0 index 
    for (var i = 1; i <= numberOfCards; i++) {

        //  Set the card rotation - always rotate from the end point so use the card number as a multiplier
        var rotation = endRotation - rotationIncrement * i;

        // The X,Y coordinates of each card.
        // Note that the origin X,Y is added to each coordinate as a hand would almost never be generated from 0,0 
        // on an HTML canvas.
        var x = radius * Math.cos(toRadians(angle * i)) + originX;
        var y = radius * Math.sin(toRadians(-angle * i)) + originY;

        // This next algorithm is used to push the cards "up" by a larger amount the further you get from the middle of the hand.  
        // This is done because a higher number of cards will start to push down and form a sharper circle. 
        // By moving them up it evens out the semi-circle to appear like something that would be more realistically held by a human.    
        // And as usual, this value is affected by existing variables to always position the hand based on its previous metrics.        
        y = y - Math.abs(Math.round(numberOfCards / 2) - i) * rotationIncrement;

        // HTML positions elements relative to the top left corner, but the CSS3 "rotation" property uses the center.  
        // So I cut the values in half and use the center to position the cards.
        // *Note: I realize both this and the previous line could have been included in the first X,Y calculation. They are separated for clarity.
        x = x - cardWidth / 2;
        y = y - cardHeight / 2;     

        // Create the card using my jQuery plugin
        var $card = $('<div></div>').card({
            width: cardWidth,
            text: i,
            rotation: rotation,
            top: y,
            left: x
        });     

        // Draw it in the parent element
        $(el).append($card);
    }
}

// Helper function to convert to radians from degrees since Javascript's Math library defaults to radians.
function toRadians(degrees) {
    return degrees * Math.PI / 180;
}

Aquí hay algunas capturas de pantalla de los resultados en acción con diferentes valores:

enter image description here enter image description here enter image description here enter image description here

2voto

Shabaz Puntos 403

En relación con el centro del círculo, las cartas están en $(-30^{\circ},-15^{\circ},0^{\circ},15^{\circ},30^{\circ})$ . Si tomamos el centro como (0,0), las coordenadas son entonces $(r \sin -30^{\circ},-r \cos -30^{\circ}), (r \sin -15^{\circ},-r \cos -15^{\circ})$ y así sucesivamente. Entonces sólo necesitas una calculadora o tablas de senos y cosenos para obtener $$\begin {array} {ccc} \\ angle & sine & cosine \\-30 & -0.5 & 0.866 \\-15 &-0.259 & 0.966 \\ 0 & 0 & 1 \\15 & 0.259 & 0.966\\30 & 0.5 & 0.866\end {array}$$ Si su centro no es $(0,0)$ Sólo hay que añadirlo a estos valores. Si tienes un número diferente de tarjetas, puedes espaciar igualmente los ángulos de $-30$ a $+30$ grados

1voto

Micah Puntos 526

Si he entendido bien tu pregunta, deberías poder aplicar una simple rotación plana, es decir: $$\begin{bmatrix} Cos(\theta) &-Sin(\theta)) \\ Sin (\theta)&Cos(\theta)) \end{bmatrix} *\begin{bmatrix} x\\y \end{bmatrix}$$ donde * denota la multiplicación de la matriz, y $\theta$ es el ángulo por el que quieres rotar. Esto rotará el punto (x,y) a lo largo de un círculo centrado en el origen.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X