Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Rotating a sprite about a point?  (Read 10657 times)

0 Members and 1 Guest are viewing this topic.

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Rotating a sprite about a point?
« on: November 23, 2012, 03:46:34 am »

transform.rotate(angle,point);

sprite.setOrigin(0,0);
sprite.setRotation(0);
sf::Rect<float> new_rect = transform.transformRect(sprite.getGlobalBounds());
sprite.setPosition(new_rect.left,new_rect.top);
 

It's rotating, just not correctly.  I placed the SetOrigin and SetRotation in my code and it's still exhibiting this behavior.

Also, I realize you're going to ask for more code, please don't.  If this code is correct for rotating about an external point, I'll investigate other things.  I couldn't really find any other way to rotate a sprite about an arbitrary point.  I need the sprite itself to rotate, not just the display of it.

edit: note that I'm rotating by 90 degrees every time, but I'm seeing it flip 180 degrees, I'm seeing it not do anything, and sometimes it does what I expect it to do, I'm just not sure why.

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Rotating a sprite about a point?
« Reply #1 on: November 23, 2012, 07:19:27 am »
You gave code out of context, with no easy way to test it, without telling what you expected nor what your observed. The edit note cleared that a bit, but it's still far from clear.

Are you trying to rotate the sprite ?
Sprite::setPosition doesn't rotate a sprite.
As a side note, Rect<float> is called FloatRect. Rect are NOT oriented. The method transform_rect is a bit weird :
Quote
Since SFML doesn't provide support for oriented rectangles, the result of this function is always an axis-aligned rectangle. Which means that if the transform contains a rotation, the bounding rectangle of the transformed rectangle is returned.

Now, you only put the sprite to the top-left of this new rectangle.

We have no idea what the "point" variable holds. Is it the center of the tile ? Any random point ?

With the code here, the sprite should never ever rotate, and in some cases, you may never see it move either.

So I'm sorry if you're afraid of complete code, but as a great man once said,
http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
AW: Rotating a sprite about a point?
« Reply #2 on: November 23, 2012, 09:47:47 am »
I'm not sure, but can't you simply set the origin to the wanted point (keep in mind it has to be relative to the sprite) and the rotate it?
Also what do you mean with 'rotate'? There are many definitions...
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #3 on: November 23, 2012, 10:22:38 am »
Am I imagining things, I thought I already responded to this thread.

Quote
I'm not sure, but can't you simply set the origin to the wanted point (keep in mind it has to be relative to the sprite) and the rotate it?
Also what do you mean with 'rotate'? There are many definitions...

That was one of my first thoughts, but it seems awfully cumbersome to do in the general case, so I figured there had to be a more canonical way to do it.  I've gotten the rotation to work several different ways I just haven't really been happy with any of them.

*is* that the canonical way to do rotations?  What happens when you're doing a lot of rotations against points that move around instead of being fixed relative to the sprite? 


gyscos, here's the abridged version of what I thought I had already posted:

I'm building a tetris clone, the sprites are blocks, and they're always axis-aligned, that's specifically why I'm using transformRect.  After the translation, there is potentially a new 'top left corner' due to the rotation, so using the behavior of the TransformRect was convenient (versus transforming just the original top left point).

What I really want to do is rotate the sprite around a point external to the sprite (the 'center' of the tetrimino).  I realize I can hand a transform to the draw function of the RenderTarget, but seeing as I need to be able to grab the positions of the sprites *after*, I would prefer to keep the position information in the sprites themselves rather than doing a TransformPoint on their position every time I need the global position of a sprite, or worse, having to track their position separately, at which point, what is the use of the sprite?


gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Rotating a sprite about a point?
« Reply #4 on: November 23, 2012, 12:17:16 pm »
Well, that's not really the correct way to do it then...
First, why don't you just rotate a point ?

Set the sprite's center to be the center of the block ; then rotate the targeted center, and set this new position... It's a much more logic way to do it than rely on bounding boxes...

When I did a Tetris clone, I just had 4 arrays of booleans for each tetromino, representing the block/no-block state the piece in each configuration. No need for rotation, it's much simpler and faster (I hate sinuses at runtime). Just automatically create these arrays at the beginning of the game, and you're good to go - it's even quite easy to use it with a vertex array.

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #5 on: November 23, 2012, 08:06:32 pm »
gyscos I appreciate your help.


But what I was looking for was an answer to the following:

How do you normally rotate a sprite about an arbitrary point?

If I really need to completely change an approach that's worked in numerous other libraries, then that's a failure.  I'm not here simply to get Tetris to work, I could calculate the rotations manually if that were the case.  I want to learn how SFML expects it to be done.


Does that mean recalculating the origin every time?  If so, that's ok, but confirmation would be appreciated.

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Rotating a sprite about a point?
« Reply #6 on: November 23, 2012, 09:11:10 pm »
You're somehow contradicting yourself. Also, you seem pretty loose on the critics.

What do you exactly mean when you say

Quote
How do you normally rotate a sprite about an arbitrary point?

One thing is certain, that's not what you seem to want. You are not using any rotation method. You are using setPosition, which is a translation method.

Since your sprites are blocks, maybe you don't really want to rotate them, but instead translate them to a rotated position. This would make sense considering what you said in the first few posts, hence my suggestions on better ways to do it, but now that last message makes it even more unclear.

But maybe I was wrong, and maybe you don't want that. Maybe you want to rotate the sprite itself, because it doesn't quite have a rotational symetry. But since the Sprite class provides easy method for that, I will assume that's not what you really want either.

What you appear to want is both translation and rotation. That any combination of translation and rotation can be expressed as a single rotation doesn't mean it is a good way to represent the transformation.

In a semantic way, you rotate the Sprite to the correct orientation, then you translate it to the current position. The sprite's origin of rotation is its real center, that is, the center of the block. This is such a classic representation that I'm a bit scared when you say you got the wrong way working with many other libraries - that they didn't make you feel it was wrong is a bit sad.

Now, maybe you still want some tricks to be able to do you thingy-thingy with your magic rotation. Just make a wrapper around Sprites, or give the transform at the draw call, or any one of the thousands of ways to do it. In the end, if you don't want to rotate the texture itself, it'll all be the same.

Quote
If I really need to completely change an approach that's worked in numerous other libraries, then that's a failure.
If you expect any library to behave the same way, with the same idioms and conventions and whatnot, then you clearly are off to a bad start - and more.
« Last Edit: November 23, 2012, 09:14:38 pm by gyscos »

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #7 on: November 24, 2012, 01:46:14 am »
Quote
If you expect any library to behave the same way, with the same idioms and conventions and whatnot, then you clearly are off to a bad start - and more.

I think it's fair to point out that I'm here asking when I could have simply done the calculations myself and manually set the position.  I've also specifically asked what the recommended method for doing it in SFML is.

Having said that, the mathematics behind this stuff doesn't change, only the API calls and the programmatic techniques used to describe them.


Quote
You're somehow contradicting yourself. Also, you seem pretty loose on the critics.

I don't understand the term 'loose on the critics', so I can't really respond to your statement except to say I don't feel like I'm contradicting myself, so perhaps some clarification about what you feel is contradictory would help me explain things.


So with that in mind, I'll try to describe what I'm doing in a little more depth, perhaps it will become clearer.

I describe the tetriminos in terms of:

4 vectors,
1 center,
X number of rotations.
Plus whatever resources the particular library needs for displaying images, etc.

Everything is described in terms of blocks of size 1.

The center is specified because tetrimino's don't rotate about the true center, they rotate about a specific block, so define the center of that block as the center for the tetrimino.

tetriminos also don't simply rotate, they rotate in specific patterns.  The I tetrimino, for example, starts sideways, swings down, swings back up, swings down, swings back up.  It does not simply rotate all the way around.

So we have an int vector describing the rotations.  The 'I' you see is an enum, it's used to load the appropriate resource.  And yes, I realize that's a horrible enum name.

Here is the function describing my I tetrimino.

Tetrimino createI() {
    sf::Vector2f vecs[] = {
      sf::Vector2f(-2.0, 0.0),
      sf::Vector2f(-1.0, 0.0),
      sf::Vector2f( 0.0, 0.0),
      sf::Vector2f( 1.0, 0.0),
    };
    std::vector<int> rotations;
    rotations.push_back(90);
    rotations.push_back(-90);

    return buildTetrimino(vecs,I,sf::Vector2f(-0.5,0.5),rotations);
  }
 


I'm not going to post the BuildTetrimino function, suffice it to say, it loads up some resources, creates sprites, and hands them to the tetrimino constructor.

Here is the constructor.  As you can see, I'm scaling out based upon the blockSize, and then setting the scale for the actual sprite (the textures are 64x64).


Tetrimino::Tetrimino(sf::Sprite inpBlocks[], sf::Vector2f center,std::vector<int> rotations, int blockSize)
  : rotationIndex(0),rotations(rotations)
{
  this->center.x = center.x*blockSize;
  this->center.y = center.y*blockSize;

  for(int i = 0; i < 4; i++) {
    this->blocks[i] = inpBlocks[i];
    sf::Sprite *b = &this->blocks[i];
    sf::Vector2f sz(b->getTexture()->getSize());

    b->setPosition(blockSize*b->getPosition().x, blockSize*b->getPosition().y);
    b->setScale(blockSize/sz.x,blockSize/sz.y);
  }

 
  this->translateToTopLeft();
}
 


And here is the function to rotate.

void Tetrimino::rotateForward() {
  if(this->rotations.size() == 0) return;

  this->rotationIndex = (this->rotationIndex+1) % this->rotations.size();
  int angle = this->rotations[this->rotationIndex];

  this->rotateBlocks(angle);
}
 



I have tried several methods, some of them I've gotten to work, some of them I haven't.

1) I've tried setting the origin of all the blocks to the center of the tetrimino and then just rotate them, it was actually my first solution.
   - it didn't work, they did some wierd dance that I couldn't explain.  I think the position I'm assigning to them is interacting with the origin in some manner I didn't expect.  I'd have to think about it.
   - I don't necessarily like this approach due to the origin being defined in local coordinates.  It actually works well for this particular application, but in general I don't feel like it's a good approach.

2) I've tried defining a transform for the tetrimino itself and just applying transforms for all moves, rotations, etc, and on draw of the sprites, handing that transform to the call to RenderTarget::draw
   - it worked flawlessly.
   - I don't necessarily like it.  At the end of the day, I need to be able to grab the positions of the blocks, which means using that transform on each block every time, and grabbing the top left vector of the bounding box, or storing the information separately.

3) As you saw, I tried using the transform as defined in 2, to recalculate the top left corner, and grab the new top left corner (of each block).  You're right that the center would be more convenient, but I would have expected it to work regardless, and it didn't.  The tetrimono *would* rotate properly, but the rotations were not correct.  What I mean by that is rotating by 180 degrees instead of 90.
  - I actually don't dislike this approach as much as I should.  It's a bit hackish, but the recalculation only happens on rotation, the information is stored in the sprite. I can sequester the ugliness to a single function, and everything else in the tetrimino object can treat the blocks as if we're not being ugly and hackish about the rotations.  ie, no one else needs to have knowledge that all is not perfect within the world of our blocks.

4) I'm considering doing away with the sprites entirely and just having the tetrimino store the 4 vectors, and then using an approach similar to 2 above to track everything.
   - I believe it would work, and most likely the code would be cleaner
   - It feels like I'm not taking advantage of tools that are there for me to use.  Not using sprites is all well and good, but if I *do* decide to keep using this library, I really need to understand what use cases the sprite class was meant for.  Because my use case is pretty simple, but it feels like sprites aren't a good fit, in which case ... what are they useful for (and that goes for the entire Transformable idea).

5) manually doing the calculations.  I would be done by now had I taken this approach
   - I'm not using the tools in SFML, in which case I'm not learning, so why am I doing it?  To write *another* tetris clone?


So there you have it, a brain dump.

also, fyi:  this is how I learn new things.  I do stupid shit, see the result, and then do more stupid shit, until I start to get a feel for the possibilities.  I ask questions, I don't necessarily want a fix to the code, I want answers that help me understand the broader picture.

And then I go off and do more stupid shit.
« Last Edit: November 24, 2012, 02:02:16 am by ichineko »

gyscos

  • Jr. Member
  • **
  • Posts: 69
    • View Profile
Re: Rotating a sprite about a point?
« Reply #8 on: November 24, 2012, 08:01:01 am »
You know, I don't really care about your code. I pointed flaws in your questions, and instead of understanding them and answering, you just showed raw code and hoped it would answer. Bad news, it didn't.

The main question is still there.

Do you want each square block to actually rotate when a tetromino (where did Tetrimino come from ??) rotates ? Or do you just want to move the block to their new positions ? This last method is what is actually used in many existing tetris games, but I understand you may want the first one nonetheless.

But in both cases, the idea is the same : you translate the block to the correct position, and eventually rotate it around the block's center (NOT the tetromino's). The order doesn't matter.
The translation is where the "tetromino rotation" will take place. Just use Transform::transformPoint to get the new position for each block.

This all is a trivial issue, and you are making it incredibly unclear, stating past failures and so on. Rotation twice what it should have been ? Can't be SFML's fault, so as a description of a bug, it's pretty vague. Just read your first post again...

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #9 on: November 25, 2012, 01:45:42 am »
Quote from: gyscos
So I'm sorry if you're afraid of complete code, but as a great man once said,
http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368

Quote from: gyscos
You know, I don't really care about your code.

So at this point it's fairly obvious that nothing I give you will be enough.  You don't have to explain, I understand.  I'm not angry, it is what it is.

If someone else wants to step in and make a comment, I'm all for it, I appreciate any input I can get.  But otherwise, this will be my last post on this topic.  When someone becomes so obviously biased, the only thing left to do is walk away or be pulled into a pissing contest, which I have no interest in.


I think what I'm going to do is go with approach #4, and just not use the Transformable APi at all.  I do like the Transform API, and I think the Drawable API is pretty good, honestly.  But the Transformable API has absolutely no sense of order of operations, and I find myself struggling to deal with that, especially when the transforms they're based on are non-commutative, and therefore order of operations is paramount.

Laurent, I urge you to reconsider the Transformable design.  It requires you to understand what a Transformation Matrix is, yet it breaks the expectations one has about Transformation Matrices.  I understand the problem that was trying to be solved, but I don't think that was a good solution.  I would almost bet money that no one actually *uses* that API for anything more than trivial work.  I also think you should atleast make the Texture copy constructor explicit, but I get the feeling you won't ;)


gyscos,

There is a document called the 'tetris guideline' (http://tetris.wikia.com/wiki/Tetris_Guideline) that specifies, among other things, how the tetriminos rotate, their color, starting position, how they're randomly generated, and so forth.

For the most part, I've kept fairly accurate with it, although, coincidentally, the way the I rotates isn't standard.  I found I preferred the way it rotated in http://www.freetetris.org/, so that's how I implement it now.  I also don't implement wall kicks or the infinite spin as I think those are lame :)

Of particular interest, under the section 'Indispensable rules' of the guideline I linked above:

Terms used in the user manual: "Tetriminos" not "tetrominoes" nor "tetrads" nor "pieces", letter names not "square" nor "stick", etc.


Also, the wikipedia article you linked:

http://en.wikipedia.org/wiki/Tetromino

A popular use of tetrominos is in the video game Tetris, where they are often called Tetriminos.



You are correct that the mathematical term is tetromino, but years ago I decided I wanted to be *fairly compliant*, and so even the verbiage has stuck with me.


ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #10 on: November 25, 2012, 09:01:46 am »
So for anyone curious, I ended up just throwing away the use of sprites, and dealing with the vertices directly.  I had it rotating like a champ in roughly 15 minutes.

It just reinforces my opinion that the Transformable API needs to be taken out back and put down like the dog it is.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Rotating a sprite about a point?
« Reply #11 on: November 25, 2012, 09:31:11 am »
Laurent, I urge you to reconsider the Transformable design.  It requires you to understand what a Transformation Matrix is, yet it breaks the expectations one has about Transformation Matrices.
You understand sf::Transformable in a wrong way. It is not a wrapper around a transform matrix, that's what sf::Transform does. sf::Transformable is a convenience base class for high-level classes like sf::Sprite or sf::Text, so that the user has the most common functionality at hand and can build its own hierarchy. If you need more flexibility, take sf::VertexArray and sf::Transform -- they have been introduced exactly for that reason.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #12 on: November 25, 2012, 10:30:27 am »
I'm sure you're right Nexus, there's just something about the behavior of the Transformable class that I find difficult to reason about.  I think it's the lack of order of operations.

Using the vertices and transforms directly just feels more natural to me, it's easier to reason about the behavior.  With the Transformable, I have to keep reminding myself there is no order.  The abstraction just doesn't feel right to me.

edit: also, it was the behavior of Sprite that originally got me looking closer at the Transform class.  Also, I feel like any abstraction that naturally causes your circle shape to draw from the upper left hand corner is suspect.  That's not a show stopper by any means, but I can't be the only one that started scratching my head the first time they encountered that, lol.
« Last Edit: November 25, 2012, 10:34:09 am by ichineko »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Rotating a sprite about a point?
« Reply #13 on: November 25, 2012, 10:36:34 am »
The order of operations in sf::Transformable is well-defined: scale, rotate and translate -- all based on the origin.

The goal of this class is to provide a simpler API that doesn't deal with transform matrices, but with transformation components that can be manipulated individually (that's what a lot of APIs do, by the way). You want to rotate your sprite by 45°? Call rotate(45), you don't have to care about what other transformations are/will be applied to the sprite, the result will be consistent.

So yes, this is a convenience API for people with simple requirements.

The point is that SFML offers several levels which can interact with each others. You clearly need sf::Transform, but you can use it with sf::Sprite and ignore its built-in transformable API. You're not forced to use vertex arrays (although it is the ultimate solution for experienced users).

You know, if I had to create something with SFML, I'd probably use only vertex arrays and transforms. The rest exists for historical/convenience/simplicity reasons.
« Last Edit: November 25, 2012, 11:00:13 am by Laurent »
Laurent Gomila - SFML developer

ichineko

  • Newbie
  • *
  • Posts: 44
    • View Profile
Re: Rotating a sprite about a point?
« Reply #14 on: November 25, 2012, 11:08:45 am »
What I meant by order of operations is that, for example, I can't translate1, rotate, translate2.   The first translate will just never happen.

And I realize the API is called 'move' and 'setPosition', but in my head it's still just a glorified translate.

Quote
The point is that SFML offers several levels which can interact with each others. You clearly need sf::Transform, but you can use it with sf::Sprite and ignore its built-in transformable API. You're not forced to use vertex arrays (although it is the ultimate solution for experienced users).

Maybe I missed something, but I tried that too.  The getTransform() is const, so I can't call Transform::combine on it, and I don't recall seeing any constructors for Sprite that took a transform.

Unless you mean by passing a transform in directly to RenderTarget::draw?  That did work, it just feels clunky to me.  Not the draw aspect, I like that API, just having to do that instead of dealing directly with the sprite feels clunky.

If I missed something, please let me know, but otherwise I doubt I'll be using the Transformable API.  For me it isn't about simple needs as much as I understand the use of the Transform stuff better than the Transformable stuff.  The limitations of it are a bit wacky to me.


 

anything