SFML community forums

Help => Graphics => Topic started by: Garwin on April 05, 2023, 10:36:08 pm

Title: View zoom and text
Post by: Garwin on April 05, 2023, 10:36:08 pm
I would like to create world map which has a lot of labels. This map can be rotated, moved and zoomed and for that view is perfect. However these labels should not be zoomed.

What can be best solution with SFML?

I am thinking to have the second view for these labels only. However it means, that view cannot use zoom. That zoom must be transferred to move relating to the position of center of the view and applied individually to the texts and view applied on the text can use same moving as world map view and rotation skipped.

Another idea is to use transform matrix on position of object with world map view to get pixel on screen and than render text on that position.

Or is there another better way to do it?

Title: Re: View zoom and text
Post by: eXpl0it3r on April 07, 2023, 12:29:17 pm
Having text scale and move smoothly with a zooming and moving map is not a trivial task, so it will always require a bit of math for the positioning.
The most important part about text rendering is, to not scale the text itself, otherwise you get blurry text. When you want to make text bigger, you need to scale the font size instead. You may get away with making large text smaller, but it can still introduce artifacts and unclean text boundaries.

Personally, I'd probably go with a separate view and calculate the position and size of each text to be perfectly matching the objects in the map. It gives complete control over the sizing of the text, and you don't have to adjust for world zoom/rotation etc.
Plus it also allows you to focus on texts that are currently visible.
Title: Re: View zoom and text
Post by: Garwin on April 07, 2023, 02:40:42 pm
Personally, I'd probably go with a separate view and calculate the position and size of each text to be perfectly matching the objects in the map. It gives complete control over the sizing of the text, and you don't have to adjust for world zoom/rotation etc.
Plus it also allows you to focus on texts that are currently visible.

I am trying to prototype this world's behavior. The moving entities work fine and the text is as it should be - placed over the top left corner with offset relating to the text size.

sf::Vector2f transformText (const sf::Vector2f& position, const sf::RenderWindow& window, const sf::View& viewFrom, const sf::View& viewTo)
{
    sf::Vector2i positionInWindow = window.mapCoordsToPixel (position, viewFrom);
    return window.mapPixelToCoords (positionInWindow, viewTo);
}

text2.setPosition(transformText(entity2.getPosition() + textOffset, window, mainView, textView));
 

When I tried to start zooming and rotating the view, the text follow the entity's left corner, however, as the entity is rotating the top left corner is not the top left anymore so with 180° rotation the text is under the entity.

I understand that I need to take the entity to apply the transformation of the view and get the bounding box of the entity and use the top left corner as a place for the text before offset.

Can SFML help me with that? The function transformText transforms a single coordinate but I need the whole entity to get global bounds. Can I achieve that without making a shadow copy of the entity to just have this entity with view transformation to achieve global bounds and that top left corner?

note: the whole prototype is in the attachment

Title: Re: View zoom and text
Post by: Garwin on April 10, 2023, 12:56:34 am
I used this for CircleShape. It works fine.

sf::Vector2f getPositionForText (const sf::CircleShape& shape,
                                                  const sf::RenderWindow& window,
                                                  const sf::View& viewFrom,
                                                  const sf::View& viewTo)
{
    float radius = shape.getRadius();
    sf::Vector2f center {shape.getPosition().x, shape.getPosition().y};
    sf::Vector2i positionInWindow = window.mapCoordsToPixel(center,viewFrom);
    center = window.mapPixelToCoords (positionInWindow, viewTo);
    center += sf::Vector2f {-radius * shape.getScale().x, -radius * shape.getScale().y}
                         * (viewTo.getSize().x / viewFrom.getSize().x);
    return center;
}

// and calling this function

text3.setPosition(getPositionForText(entity2, window, mainView, textView)
     + sf::Vector2f{0.f, -textSpaceHeight}); // text offset
 

Thinking about doing similar for any sf::Shape or VertexArray. I am thinking about 2 ways:
1. through bounding box
- getting sf::FloatRect bounding box of the entity
- transform between views using sf::Transform
- getting a new bounding box from a transformed bounding box
Advantages: relatively simple and probably reasonably fast
Disadvantages: position of text will be simplified as in the bounding box from the transformed bounding box could be large, especially if the transformed bounding box is at 45°.
2. through transforming entity
- copy entity
- transform entity between views
- finding the bounding box from the transformed entity
Advantages: Much more precise as the bounding box is found on top of transformed entity
Disadvantages: If there are many text labels over an entity it practically make no sense for the view and all these entities need to be copied transformed one by one