4
$\begingroup$

Scenario: I'm programming a module to draw a deck of cards on a canvas as though they were being held by a person.

Edit:

I've cleaned up the question as best I can to be clearer.

What I'm looking for now:

The X,Y coords of each star, which represent the center of each card, as well as its rotation from that point to form a semi-circle.

To make it simpler than before, you can now also assume:

  1. The total angle the cards cover is 90º.
  2. There are 5 cards.
  3. The middle card will always be at X = 0
  4. Each end card will be on a rotation of 30º (the left side will be -30º)
  5. The canvas is a fixed size. Note: I put 200 as the height, though this is somewhat of an arbitrary number and I don't even know if it helps or not.

I should also mention that drawing is not exactly to scale, I did the best I can with a somewhat primitive tool for drawing geometric shapes


Solution:

The source for my solution can be found here: https://github.com/tcrosen/playing-cards

A working demo can be found here: 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($('X').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 = $('
').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; }

Here are some screenshots of the results in action with different values:

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

  • 0
    Rewrote question for clarity2012-03-28

2 Answers 2

2

Relative to the center of the circle, the cards are at $(-30^{\circ},-15^{\circ},0^{\circ},15^{\circ},30^{\circ})$. If we take the center as (0,0), the coordinates are then $(r \sin -30^{\circ},-r \cos -30^{\circ}), (r \sin -15^{\circ},-r \cos -15^{\circ})$ and so on. Then you just need a calculator or sine and cosine tables to get $\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}$ If your center is not $(0,0)$, just add it to these values. If you have a different number of cards, you can just equally space the angles from $-30$ to $+30$ degrees

  • 0
    Updated my question with answer details, thanks for your help.2012-03-29
1

If I have understood your question properly, you should just be able to apply a simple planar rotation, ie: $\begin{bmatrix} Cos(\theta) &-Sin(\theta)) \\ Sin (\theta)&Cos(\theta)) \end{bmatrix} *\begin{bmatrix} x\\y \end{bmatrix}$ where * denotes matrix multiplication, and $\theta$ is the angle you want to rotate by. This will rotate the point (x,y) along a circle centered at the origin.

  • 0
    The OP's coordinates are not standard-the origin of angles is up and positive is clockwise. It still works, but the vector should be $\begin{bmatrix} 0\\r \end{bmatrix}$2012-03-28