23
\$\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\$
3
  • 9
    \$\begingroup\$ Sir, you seem to have too many aces in your hand. Please step away from the table. \$\endgroup\$ Jan 14, 2012 at 18:53
  • \$\begingroup\$ The fiddle needs to be updated to use a later version of MooTools. \$\endgroup\$
    – tomdemuyt
    Feb 16, 2018 at 17:00
  • \$\begingroup\$ Related: How can I approximate an “opening fan”-transformation? \$\endgroup\$
    – Anko
    Mar 5, 2019 at 14:09

1 Answer 1

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\$
3
  • 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\$ Jan 9, 2012 at 21:24
  • \$\begingroup\$ @eBusiness You're right, I'll add that in. \$\endgroup\$ Jan 9, 2012 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, 2012 at 21:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .