Access the flipping property of an image object

hello,
I have been looking the forum about this topic but couldn’t find it. I have an image object which properties I am accessing. I use this code

auto layer = Layer::create();
TMXObjectGroup* tmxObjGroup = tileMap->getObjectGroup(layerName);  // object groups refers to the object layer
 for (auto obj : tmxObjGroup->getObjects())
    {
        auto dict = obj.asValueMap();
        // Check object properties
        for (auto elem : dict)
        {
            log(">> %s", elem.first.c_str());
        }  
    }

The output is this

>> rotation
>> id
>> height
>> name
>> x
>> type
>> y
>> gid
>> width

All properties show up except “flipping” that I can’t see anywhere. I guess “flipping” should be of a container type variable or something like that, because it contains “horizontal” and “vertical” as bools.
Is it possible to access the “horizontal” and “vertical” bools?

Also, does Tiled has an output window that shows in code live what you are doing? For instance, in 3d softwares if you move something in the viewport you see live the corresponding command?

thx

2 Likes

The GID encodes the flipping flags and the tileset for the tile. You can read more about GIDs in the documentation.

Tiled has no live code view. In any case, any such view would, at best, show the JavaScript Tiled API, which is different from the APIs of libraries for parsing TMX files, for example there are no GIDs at all.

2 Likes

:exploding_head: mmmkey. I read the documentation and it’s code example. I did not understand anything XD.
Just on the first sentence I got lost: T

he highest four bits of the 32-bit GID are flip flags, and you will need to read and clear them before you can access the GID itself to identify the tile

I think I’ll just manually add a “flippedHorizontal” boolean property to the image and check it if flipped. Currently it’s a bit advanced for me to really understand that doc.
.

To explain it at a lower level:
A GID is a 32-bit int. The highest 4 bits are flip flags. So for example, if the GID is 2684354563, the bits for that are 1010 0000 0000 0000 0000 0000 0000 0011
The highest bits are the ones on the left (the 1010 part). The leftmost one is horizontal flip (1 in this case, which means the tile is flipped horizontally). The next one is vertical flip (0, so the tile is NOT flipped vertically). The third one is the anti-diagonal flip (used for rotations) (it’s 1 so it’s true). The last one is the hex rotation flip flag (0 so false in this case).
You can extract the individual bits with the bitmasks in the code (the FLIPPED_HORIZONTALLY_FLAG, etc parts). Then after you’ve read those bits, you can clear them using those same bitmasks, which leaves you with just the global tile ID: 0000 0000 0000 0000 0000 0000 0000 0011 = 3.

Even if you use a custom property, you still need to clear the flip flags to avoid ending up with invalid tile IDs, so it’s a good idea to understand how GIDs work. And once you do, you won’t need a custom property for flips.

Okey, I’ll give that I thought. My problem is that I can get my head around what I currently have which is is this:

auto layer = Layer::create();
TMXObjectGroup* tmxObjGroup = tileMap->getObjectGroup(layerName); // object groups refers to the object layer
for (auto obj : tmxObjGroup->getObjects())
{
auto dict = obj.asValueMap();
auto gid = dict[“gid”].asInt(); /
}

and what you explained.
I mean, when I print out the gid I just get an integer. So I am not sure where to go from there to what it’s explained in the doc. I am sure this must be quite obvious with a better understanding. I’ll try to figure it out.
Thank you.

You have the integer, now you just need to do some work on it to extract the the flip flags and the global tile ID. This integer is stored in memory in binary form, i.e. as a bunch of bits, just like all other data. But unlike regular numbers, in this case, five separate pieces of data are stored in there: the four flags, and the GID.

Maybe it’ll help to explain this with a decimal example: You might’ve encountered apartment buildings before where the apartments on the first floor are numbered from 101, the ones on the second floor are numbered from 201, the ones on the 3rd floor are numbered from 301, and so on. In this case, the leftmost digit of an apartment number indicates the floor the apartment is on, and only the digits after that are actually numbering apartments. So, “514” means “apartment 14 on the 5th floor”. GIDs work similarly, except with 32 digits (in binary). The first four bits (binary digits) are like the floor numbers, and the rest are the apartment number.

If you just print the GID int, you’ll probably get a decimal representation, which makes it hard to see the structure. But if you look at the binary representation, you can see the leftmost four bits correspond to the flags, and the rest of the bits represent the global tile ID (and these just count up, like the apartment numbers on a given floor).
Just like you can look at a specific digit in a 3-digit apartment number, you can do the same for a specific bit in a binary number. The example code in the documentation shows how to do this using bitmasks, which are the standard technique. A bitmask, just like a masking tape or a paper template, lets you block out the bits you’re not interested in and read only the ones you care about. If you’re interested in how this works, look up bitwise logic - though not a complex topic, it’s nonetheless beyond the scope of a Tiled forum post, and there are many resources that present the ideas in different ways, maybe you’ll find one that clicks for you.

So, in the apartment example, if you wanted to know what floor an apartment is on, you’d check the leftmost digit. Then, to know how far down the hall to go, you’d ignore/clear that leftmost digit and look at just the remaining ones. So, for apartment 514, you’d go to the 5th floor, and then go until you get to the 14th door (or, more realisitcally, until you find the one labelled “514” xP This analogy is imperfect).

In the Tiled case, you want to know what flip flags a tile has, so you check the first four bits. Then, you ignore/clear those and look at the remaining number to get your actual global tile ID.

Hopefully this makes the example code clear. I don’t know what language you’re using, but its syntax for bitwise logic is probably similar, so the example code should serve as decent reference for your code.

1 Like

Thanks for taking the time to explain it. Appreciate that.
I think I mentioned that in other posts, I am using Axmol which is c++.
Okey, I’ll give that a try again and hopefully post the working code in case anyone else needs it.

1 Like

The example in the documentation is C++, so you should be able to use the relevant parts directly :D

auto gid = dict[“gid”].asInt(); //from your code

// Read out the flags
bool flipped_horizontally = (gid & FLIPPED_HORIZONTALLY_FLAG);
bool flipped_vertically = (gid & FLIPPED_VERTICALLY_FLAG);
bool flipped_diagonally = (gid & FLIPPED_DIAGONALLY_FLAG);
bool rotated_hex120 = (gid & ROTATED_HEXAGONAL_120_FLAG);

// Clear all four flags
gid &= ~(FLIPPED_HORIZONTALLY_FLAG |
         FLIPPED_VERTICALLY_FLAG |
         FLIPPED_DIAGONALLY_FLAG |
         ROTATED_HEXAGONAL_120_FLAG);

//the four bools now contain your flip flags, and gid contains the global tile ID
//Use/store the bools however you like, and then resolve the gid to its tileset and tile index.

The four bitmasks (FLIPPED_HORIZONTALLY_FLAG, etc) are constants that you can define somewhere outside the loop, or replace directly with their values. You can get their values in the documentation example code. The values just correspond to having the relevant bit set to 1, and the rest set to 0.

1 Like

Cool, great. Now we are getting somewhere but there something strange.
This is the code:

auto layer = Layer::create();
    TMXObjectGroup* tmxObjGroup = tileMap->getObjectGroup(layerName);  // object groups refers to the object layer
    for (auto obj : tmxObjGroup->getObjects())
    {
        auto dict = obj.asValueMap();
        auto spriteImagePath = dict["imagePath"].asString();
        log(">>>>> %s", spriteImagePath.c_str());
        auto sprite = Sprite::create(spriteImagePath);
        auto gid = dict["gid"].asInt();
        log(">> gid: %i", gid);

        // Bits on the far end of the 32-bit global tile ID are used for tile flags
        const unsigned FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
        const unsigned FLIPPED_VERTICALLY_FLAG = 0x40000000;
        const unsigned FLIPPED_DIAGONALLY_FLAG = 0x20000000;
        const unsigned ROTATED_HEXAGONAL_120_FLAG = 0x10000000;

        // Read out the flags
        bool flipped_horizontally = (gid & FLIPPED_HORIZONTALLY_FLAG);
        bool flipped_vertically = (gid & FLIPPED_VERTICALLY_FLAG);
        bool flipped_diagonally = (gid & FLIPPED_DIAGONALLY_FLAG);
        bool rotated_hex120 = (gid & ROTATED_HEXAGONAL_120_FLAG);

        log("flipped_horizontally: %i", flipped_horizontally);
        log("flipped_vertically: %i", flipped_vertically);
        log("flipped_diagonally: %i", flipped_diagonally);
        log("rotated_hex120: %i", rotated_hex120);
        log("---");

        // Clear all four flags
        gid &= ~(FLIPPED_HORIZONTALLY_FLAG |
            FLIPPED_VERTICALLY_FLAG |
            FLIPPED_DIAGONALLY_FLAG |
            ROTATED_HEXAGONAL_120_FLAG);
}

and this is the output


On the left you can see that in Tiled I checked both flags, but the output doesn’t is still 0 for the horizontal flip.

2 Likes

Define the constants outside of the loop, I mentioned this. Constantly redefining them is a bit silly xP

As for the actual bug: Your GID appears to have been read incorrectly. It sounds like perhaps asInt() is returning a signed int and when the value is too large, it just clips it - 2147483647, the supposed value of GID, is the largest positive value a signed 32-bit int can represent, but when the horizontal flip flag is set, the resulting integer is larger. You need to read the GID as a 32-bit unsigned integer to be able to accurately store all the bits.

1 Like

Sure, I just copy pasted everything inside the loop. Now this seems to work:

// Bits on the far end of the 32-bit global tile ID are used for tile flags
    const unsigned FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
    const unsigned FLIPPED_VERTICALLY_FLAG = 0x40000000;
    const unsigned FLIPPED_DIAGONALLY_FLAG = 0x20000000;
    const unsigned ROTATED_HEXAGONAL_120_FLAG = 0x10000000;

    auto layer = Layer::create();
    TMXObjectGroup* tmxObjGroup = tileMap->getObjectGroup(layerName);  // object groups refers to the object layer
    for (auto obj : tmxObjGroup->getObjects())
    {
        auto dict = obj.asValueMap();
        auto spriteImagePath = dict["imagePath"].asString();
        log(">>>>> %s", spriteImagePath.c_str());
        auto sprite = Sprite::create(spriteImagePath);

        auto gid = dict["gid"].asInt64();  // this fixes the bool outputs
        log(">> gid: %i", gid);

        // Read out the flags
        bool flipped_horizontally = (gid & FLIPPED_HORIZONTALLY_FLAG);
        bool flipped_vertically = (gid & FLIPPED_VERTICALLY_FLAG);
        bool flipped_diagonally = (gid & FLIPPED_DIAGONALLY_FLAG);
        bool rotated_hex120 = (gid & ROTATED_HEXAGONAL_120_FLAG);

        log("flipped_horizontally: %i", flipped_horizontally);
        log("flipped_vertically: %i", flipped_vertically);
        log("flipped_diagonally: %i", flipped_diagonally);
        log("rotated_hex120: %i", rotated_hex120);
        log("---");

        // Clear all four flags
        gid &= ~(FLIPPED_HORIZONTALLY_FLAG |
            FLIPPED_VERTICALLY_FLAG |
            FLIPPED_DIAGONALLY_FLAG |
            ROTATED_HEXAGONAL_120_FLAG);
}

I also tried as this types. They work too.
auto gid = dict[“gid”].asUint();
auto gid = dict[“gid”].asUint64();
auto gid = dict[“gid”].asUnsignedInt(); <== no idea how this is different from asUint();

axmol Tilemap parser doesn’t provide as 32-bit unsigned, so I guess I’ll just use asUint64();

I still get big negative values with the gids that have any flipped set as true, but all printed boolean outputs are correct. Is this something to be concerned?

1 Like

Does asUint()/asUnsignedInt() work? Those should be 32-bit unsigned ints. They should theoretically work, and would use less memory than Uint64/Int64. But if none of the 32-bit options work, 64bit is fine, especially if you bring it back down to 32bit for actual storage after you’ve gotten the flags out of it. Once the flags are cleared, the value is guaranteed to fit inside a 32-bit int, signed or unsigned, and the actual bits don’t matter.

The reason the values may get interpreted as negative is that in most signed int representations, the highest bit (the horizontal flag, in our case) being 1 means the integer is negative. Depending on the integer representation used by the language, this can actually distort the other data, which is why using unsigned int is important. Signed 64-bit int would also work since the bits of any 32-bit unsigned int just look like a relatively small positive 64-bit int value, though that’s rather wasteful since a 64-bit int needs twice the memory of a 32-bit int.

1 Like

Yes, asUint()/asUnsignedInt work too. I just edited that. oh, so those are 32 bit. Cool.
Just googled what is the difference between them and I got this:
uint isn’t a standard type - unsigned int is.

So should I use asUnsignedInt ?

1 Like

If asUnsignedInt() returns an unsigned int, yes. I’m not familiar with Axmol so I don’t know what the difference between those two functions is and how it deals with types.

1 Like

Great, thanks a lot for you help.
Axmol community is not too big, but hopefully if someone else struggles with this, the post will be helpful.

Cheers, and thanks again.

2 Likes