20
\$\begingroup\$

Given a set of playing cards (rectangle images with a width and height) how can I rotate and position each one so that they appear in a 'fan' pattern, much like you would hold a hand of cards in real life. What math is needed to do this?

UPDATE

Here is the final, in browser implementation in JavaScript: https://cosmicrealms.com/blog/2013/03/16/hand-of-cards/ and http://jsfiddle.net/tyyvk/108/

\$\endgroup\$
30
\$\begingroup\$

Theory

Since you didn't specify in what platform you're implementing this, I'll give a description of the algorithm in a language-agnostic way:

  1. First stack every card on top of each other by giving them the same initial position.
  2. Then for each card apply a rotation (usually centered around one of the bottom corners, but moving this origin around will essentialy allow you to tweak the look of the fan).
  3. Increment the angle of rotation between each call, depending on the number of cards and how much you want them to be spread.

That the rotation is centered around one of the card's bottom corners (or near the corner) should be evident from looking at it:

enter image description here


Implementation

As for how to implement this, it depends on your platform. On XNA you can simply use the Origin parameter of SpriteBatch.Draw to change the center of your rotation.

Here's what I got with the following code (with a few tweaks to the origin to make it look better - basically the origin starts near the right corner and ends up near the left corner):

int cards = 20;
float range = MathHelper.ToRadians(90);
float initialAngle = MathHelper.ToRadians(-45);
float increment = range / cards;
Vector2 leftCorner = new Vector2(0, texture.Height * 0.9f);
Vector2 rightCorner = new Vector2(texture.Width, texture.Height * 0.9f);
Vector2 fanPosition = new Vector2(400, 300);
spriteBatch.Begin();
for (float angle = 0; angle < range; angle+=increment)
{
    float cardAngle = initialAngle + angle;
    Vector2 cardOrigin = Vector2.Lerp(rightCorner, leftCorner, angle / range);
    spriteBatch.Draw(texture, fanPosition, null, Color.White, cardAngle, cardOrigin, 1f, SpriteEffects.None, 0f);
}
spriteBatch.End();

And the result:

enter image description here

\$\endgroup\$
  • 7
    \$\begingroup\$ I'd add that the look can be tweaked by using a different centre of rotation, if you want to span a smaller arc you should use a rotation point that lies below the card. \$\endgroup\$ – aaaaaaaaaaaa Jan 9 '12 at 21:24
  • \$\begingroup\$ @eBusiness You're right, I'll add that in. \$\endgroup\$ – David Gouveia Jan 9 '12 at 21:44
  • \$\begingroup\$ Thanks a lot David! I've implemented the code you've shown in JavaScript here: jsfiddle.net/tyyvk/7 \$\endgroup\$ – Sembiance Jan 14 '12 at 21:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.