SFML community forums

Help => General => Topic started by: Eyfenna on May 12, 2019, 02:58:18 pm

Title: Hexagon grid
Post by: Eyfenna on May 12, 2019, 02:58:18 pm
Hello,  I got a hexagonal grid, and it does display right and I can even put textures on each hexagon. My problem is following:

In an additional std::map I store the coordinates of the central point and the x-y coordinate of the hexagon in the grid in a std::map<sf::Vector2f, sf::Vector2i> now I try to get the sf::Vector2i as return of a map search for displaying the Hexgrid coordinates in console for the one mouse if hoovering over in the window.  I'm trying to do this with find_if algorithm on the map. The function is part of the Hexgrid class which holds the hexagonal grid:

(click to show/hide)

however compiler notifies me of an error on converting pairs.

I also tried with another lambda:

(click to show/hide)

Here the compiler can't convert pair to sf::vector2f.


So after a search I found that I might have to write something extra but can't realy bend my head around what it is and why this specific thing is lacking. Thanks for a response or a hint.
Title: Re: Hexagon grid
Post by: Laurent on May 12, 2019, 05:20:42 pm
It would be very helpful to see that compiler error.
Title: Re: Hexagon grid
Post by: Hapax on May 12, 2019, 05:28:58 pm
You are checking equality using a floating point value, which is probably not your intention as that should be avoided, pretty much.

You can explictly convert between Vector2f and Vector2i by creating one and passing the other as a parameter to its constructor.
Since you need to use a Vector2 as a key for a map, it would be better to first convert it to integer values and then use that as the key. That is, use Vector2i as the map's key.


In case I presumed too much from the start, here's an explanation of why you shouldn't be checking equality using floating point values:
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Title: Re: Hexagon grid
Post by: Eyfenna on May 13, 2019, 02:09:50 am
The error message is (about translated) [same for std::map<sf::Vector2i, sf::Vector2i>]

Error C2664   "bool Hexgrid::GetGridPlace::<lambda_5b26f5554823591ff0c2ae8c47a36fad>::operator ()(std::pair<sf::Vector2i,sf::Vector2i> &) const" : Conversion from argument 1  "std::pair<const _Kty,_Ty>" to "std::pair<sf::Vector2i,sf::Vector2i> &" not possible. ConsoleApplication87   c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\algorithm   174   

Thanks for the hint with the floating point numbers.

Solved it one possible way:

I went a different way and safe the grid position (offset coordinates according to redblob site) inside the Hexagon and do an inRange comparison with the central point:

(click to show/hide)

and use this in a function inside the Hexgrid class:

(click to show/hide)
Title: Re: Hexagon grid
Post by: Elias Daler on May 13, 2019, 02:18:01 pm
The error happens, because sf::Vector2i can't be implcitly converted to sf::Vector2f. This should work:

sf::Vector2i Hexgrid::getCoordinate(sf::Vector2f avec)
{
   auto found = find_if(coordinate.begin(), coordinate.end(), [&](const std::pair<const sf::Vector2i, sf::Vector2i>& temp)->bool {return static_cast<sf::Vector2f>(temp.first) == avec; });
// or just (const auto& temp) in lambda
   return found->second;
}

Also note that in std::map the key has "const" type.

Also, maybe pass "avec" as Vector2i so not casts will be needed in lambda?
Also note that "found" is an iterator, so if find_if returns "end" iterator, calling "->second" on it will result in segfault, so you need to check if the coordinate was found or not.
Title: Re: Hexagon grid
Post by: Eyfenna on May 20, 2019, 08:18:10 pm
Thanks for the helpfull replies ;D

As I'm not so sure with Vertexarrays I went following way:
For the hexagonal grid I made a hexagon class(drawable) which holds the vertexarray to be more precise std::vector<vertex>. A templated GameGrid class holds a std::vector of Hexagons for example and generates the field.
I did some refractoring on it and except a pointer to a maploader - that holds the map information in an array - and three member function pointers on this class for the getters in this way I could remove the maploader header from the  GameGrid header.
I was wondering if that is still good c++ or overengineering things?

Well before describing it lengthily I post the source code for GameGrid and the hexagon class
The code for GameGrid template is all in a header now, moving things to an .inl when I played around with this format.
In so far the code for GameGrid is lengthy:

(click to show/hide)


Here the code for the Hexagon class:

the header
(click to show/hide)

and here the .cpp with the bodies of the memberfunction for Hexagon:

I left out the dependecy for VisualStudio
(click to show/hide)
Title: Re: Hexagon grid
Post by: Elias Daler on May 21, 2019, 11:21:44 am
Some things:

1)

virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const

Should be

void draw(sf::RenderTarget& target, sf::RenderStates states) const override

2) This will do tons of copies per each frame:

for (Fieldtype a : mGrid)

Do this:

for (const auto& a : mGrid)

3) Maybe store pmResources and other stuff you can by reference, not by pointer. Seems safer (you're passing it into ctor, anyway)

4) In my opinion, it's more useful to list members in the following order: public, protected, private. I'm more interested in seeing what I can actually call first.

5) Maybe separate implementation into ".inl" files, don't have one giant header.

6) Think about providing default values to callbacks in some other way in GameGrid. Right its constructor looks scary - too many arguments.

7) I'd store all callbacks as std::function's, not as pointers to functions - it provides a lot more flexibilty.

8 ) In hexagon class you store vertices as this:

std::vector<sf::Vertex> oneHex;
std::vector<sf::Vertex> twoHex;
 

I'd store it like this:

std::array<sf::Vertex, 12> oneHex;
std::array<sf::Vertex, 12> twoHex;
 

^ no dynamic allocations!

9) The formulas for setting hexagon vertices look scary. Pretty sure it can be simplified and don't be full of copy-pasted
Title: Re: Hexagon grid
Post by: Eyfenna on May 27, 2019, 09:22:27 pm
The post lead to me doing thinking about the code.

9) The formulas for setting hexagon vertices look scary. Pretty sure it can be simplified and don't be full of copy-pasted

Good tip, I concluded that a

const sf::Vector2f Orientation[] = { {-.5f,-0.25f},{0.f,-0.5f}, {0.5f,-0.25f},{0.5f,0.25f},{0.f,0.5f},{-0.5,0.25f}, };
(imitating a thing from redblobgames)

allows to shorten the vertix location setting code for an empty hexagon(sf::LineStrip) to

int a;
for (int i = 0; i < 7; ++i)
{
if (i - 6 >= 0) a = i - 6;
else a = i;
mBare.push_back(sf::Vertex(sf::Vector2f((mPosition.x + width*Orientation[a].x), (mPosition.y +height*Orientation[a].y))));
}
 

similar for the textured vertices location.


7) I'd store all callbacks as std::function's, not as pointers to functions - it provides a lot more flexibilty.

Done, std::bind is sort of easiert to handle than pointers to memberfunction once one has understood it. The reason I used function pointers was that callback with them came to my mind at first and std::bind doesn't appear in tutorial pages about callbacks I found on google.

With std::function<> callback I found also that I can leave out the pointer to the class. That is nice, works with the maploader.