Rotating Sprites : how does it change the size ?

Started by Baguettator, Sat 05/02/2022 22:24:03

Previous topic - Next topic

Baguettator

Hi there !

My question could be very stupid, but I have no idea of how ags handles that...

Imagine I have a DynamicSprite "f" that is a square ("rectangle" ?) with these attributes : f.Width=30, f.Height=34.

If I do : f.Rotate(90)

It makes : f.Width=34 and f.Height=30.

Right :)

BUT, if I do : f.Rotate(45)

What will be the width and height of f ?

Crimson Wizard

The width and height will be minimal necessary rectangle to accomodate the rotated image.

But why do you need to know how AGS does this? Do you want to know about the algorithm, or do you simply want to know resulting width and height? In the last case you may read DynamicSprite's Width and Height properties after rotation.

Baguettator

OK thanks !

I just wanted to know the formula to calculate the new height/width of the dynamic sprite.

Is the formula well-known ? I have to use it in scripts, with many things that have different heights and widths, so I need the generical approach.

Crimson Wizard

#3
Quote from: Baguettator on Sat 05/02/2022 22:34:51
I just wanted to know the formula to calculate the new height/width of the dynamic sprite.

But why do you need this formula? Are you resizing sprites yourself before rotating?
The manual sais that for DynamicSprite.Rotate "AGS will automatically calculate the new size required to hold the rotated image" if you pass no width and height.
That is, if you write just "f.Rotate(45);" it will be resized itself as necessary.

Baguettator

Because I use a script to calculate coordinates for tokens (to place on maps, like a tabletop game). And I have "stored" the possible coordinates for the different tiles.

So for example I can place my square at the 1st position of the tile A, with a "up" rotation, and it will automatically set it to x and y coordinates according to my script.

But if I rotate the tile, I have to rotate the tokens too. So they change their rotation (up becomes right for a 90° rotation, or becomes down for a 180° rotation, etc), and their coordinates. To change their coordinates, I need to know their width and height. I have to script it during a time when DynamicSprites are NOT created yet. So I can't use DynamicSprite.Width etc

Is the formula too complex ?

Crimson Wizard

#5
Quote from: Baguettator on Sat 05/02/2022 22:44:53
But if I rotate the tile, I have to rotate the tokens too. So they change their rotation (up becomes right for a 90° rotation, or becomes down for a 180° rotation, etc), and their coordinates. To change their coordinates, I need to know their width and height. I have to script it during a time when DynamicSprites are NOT created yet. So I can't use DynamicSprite.Width etc

Is the formula too complex ?


Oh, sorry. I was wondering if you are doing some unnecessary work for sprite rotation.

Formula is this (math):
Code: ags

new_width = (cos(angle) * old_width + sin(angle) * old_height);
new_height = (sin(angle) * old_width + cos(angle) * old_height);


In AGS script that would be:
Code: ags

new_width = FloatToInt(Math.Cos(angle) * IntToFloat(old_width) + Math.Sin(angle) * IntToFloat(old_height));
new_height = FloatToInt(Math.Sin(angle) * IntToFloat(old_width) + Math.Cos(angle) * IntToFloat(old_height));


The angle above is in radians. If you have angle in degrees, you need to first convert it using Maths.DegreesToRadians.

Crimson Wizard

That said, but I still wonder if this is necessary.

Could you save positions of the token centers instead of top-left corners? Then you won't have to calculate these coordinate changes, and realign the sprites only after they change.
This depends on what kind of map you have though.

Baguettator

Thanks a lot Crimson !

I already scripted all the tokens with their top-left corner coordinate (which is the way AGS works for buttons, and tokens are represented by buttons), and it should be very long to change it now... Maybe one day :) Surely it should be better, but it works perfectly for now.

I'm working on the rotation of the tile, will see if it works with the new formula.

Baguettator

I presume that if I only talk about 45° multipliers (45, 135, 225 and 315), these dynamicsprites should have the same width/heights ?

Crimson Wizard

Quote from: Baguettator on Sat 05/02/2022 23:13:12
I presume that if I only talk about 45° multipliers (45, 135, 225 and 315), these dynamicsprites should have the same width/heights ?

That should be correct, all mirror angles should result in the same size.

Crimson Wizard

#10
Quote from: Baguettator on Sat 05/02/2022 23:08:24
I already scripted all the tokens with their top-left corner coordinate (which is the way AGS works for buttons, and tokens are represented by buttons), and it should be very long to change it now...

I made a mistake. You do not have to use center positions. You may keep existing top-left positions (non rotated), and then adjust tile's position after it is rotated like this:

Code: ags

new_x = old_x + (old_width - new_width) / 2;
new_y = old_y + (old_height - new_height) / 2;

EDIT: fixed by /2

This will work generally if the tile is resized for any reason.

Snarky

Shouldn't you divide those widths and heights by 2, CW?


Baguettator

Hmm I didn't follow you in your thoughts... I used your formula (with cos and sin) to calculate the width/height of the token after rotating (45° or multipliers), then I simply do that :

Code: ags
//34=width when it's not rotated, 30 is the height when not rotated
int l=FloatToInt(Maths.Cos(Maths.DegreesToRadians(45.0))*IntToFloat(34) + Maths.Sin(Maths.DegreesToRadians(45.0))*IntToFloat(30));
int h=FloatToInt(Maths.Sin(Maths.DegreesToRadians(45.0))*IntToFloat(34) + Maths.Cos(Maths.DegreesToRadians(45.0))*IntToFloat(30));
int x, y; // x is the original x position for the top leftcorner of the token, y is the y position of the top leftcorner
int xporte, yporte; // Also the original coordinates of the token
// The tile width and height are equal to 250
if (nouveau==right) // the tile rotates to right, so 90°
{
  xporte=250-y-h;
  yporte=x;
}
else if (nouveau==down) // the tile rotates to down, so 180°
{
  xporte=250-x-l;
  yporte=250-y-h;
}
else if (nouveau==left) // the tile rotates to left, so 270°
{
  xporte=y;
  yporte=250-x-l;
}

Baguettator

Hmm Khalibri, what are you talking about ? What do you want to do ?

Baguettator

Hi here, I continue this thread with a new math problem I need help to solve :

let's say I have a square (same width and height). Into this square, I have a point at (x,y) coordinates (the coordinates are from square's size, so the top left corner coordinates are 0,0).

If I rotate the square by "a" degrees, how can I calculate the new coordinates for the point ? (the point rotates with the square, as if they were the same thing). Is there a formula that works generally for any "a" value ? I guess there is, but I don't know which :(

Khris

You need a 2D rotation matrix:



Code: ags
int[] RotatedPoint(int xi, int yi, int deg) {
  float x = IntToFloat(xi);
  float y = IntToFloat(yi);
  float r = Maths.DegreesToRadians(deg);
  float c = Math.Cos(r), s = Math.Sin(r); // cosine and sine
  int r[] = new int[2];
  // matrix times vector
  r[0] = FloatToInt(c * x - s * y, eRoundNearest);
  r[1] = FloatToInt(s * x + c * y, eRoundNearest);
  return r;
}

This function rotates a 2D point around the origin, so if your square is supposed to rotate around its center, you need to subtract half the side length from your coordinates before calling it, then add it back.

Example code:
Code: ags
  // square size s
  // shift origin to square's center
  x -= s / 2;
  y -= s / 2;
  int p[] = RotatedPoint(x, y, 5); // 5 degrees clockwise
  // shift origin back to top left
  x = p[0] + s / 2;
  y = p[1] + s / 2;

SMF spam blocked by CleanTalk