Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show off the power of the new variadic dimensions system #124

Open
nholthaus opened this issue May 11, 2018 · 12 comments
Open

Show off the power of the new variadic dimensions system #124

nholthaus opened this issue May 11, 2018 · 12 comments

Comments

@nholthaus
Copy link
Owner

nholthaus commented May 11, 2018

We have user-definable variadic dimensions now, but we need to develop some case studies we can document to guide users on how to take advantage of that. We probably also want some macros to simplify including new dimensions and making sure traits and things are auto-generated when a new dimension is defined.

If anyone comes up with something cool, post it here or in a PR and I'll include it in the docs w/ attribution.

@nholthaus nholthaus added this to the v3.0.0 milestone May 11, 2018
@nholthaus nholthaus changed the title Show of the power of the new variadic dimension Show of the power of the new variadic dimensions system May 11, 2018
@JohelEGP
Copy link
Contributor

I'm creating a game and its engine. Right now, it only has an "initial commit" with an empty ::main function. While working on the next commit, "open the game window," I created a type Pixels to represent a quantity of pixels. With it as the base of a strongly typed interface, opening the game window would look like

Window main{Size{Width{800_px}, Heigh{600_px}}};

Looking ahead to representing the positions of "entities" in the game's world, I decided on this library's length::meter_t. I found it a shame that my units couldn't participate in the dimensional analysis operations of this library. That'd be at least immediately useful for mapping the positions of things in the game's world to the game's window (a length::meter_t to Pixels mapping) in the expressive way that this library allows.

Then came the work on v3.0.0, which allows user-defined dimensions. I came up with something like this to replace Pixels:

namespace dimension {

    struct Display_length_tag {
        static constexpr gsl::czstring<> name{"display length"};
        static constexpr gsl::czstring<> abbreviation{"px"};
    };

    using Display_length = units::make_dimension<Display_length_tag>;

} // namespace dimension

namespace display_length {

    // Represents a quantity of pixels.
    template <class Arithmetic>
    using Pixels = units::unit<
        units::unit_conversion<std::ratio<1>, dimension::Display_length>,
        Arithmetic>;

} // namespace display_length

using display_length::Pixels;

inline namespace literals {
    inline namespace pixels_literals { //

        constexpr Pixels<double> operator""_px(long double px) noexcept
        {
            return Pixels<double>{gsl::narrow_cast<double>(px)};
        }

        constexpr Pixels<int> operator""_px(unsigned long long px) noexcept
        {
            return Pixels<int>{gsl::narrow_cast<int>(px)};
        }

    } // namespace pixels_literals
} // namespace literals

The user-defined dimension display length has a base unit of pixels, as indicated by its "px" abbreviation. Of course, following the good practices when using third-party libraries, this is done within my own namespace. I mirrored this library's namespace structure and use of facilities with some added conveniences.

This definition of Pixels is as well-supported as any unit in this library. It has access to the expressiveness and dimensional analysis that the library-defined units have. I found it great to have access to so many useful things in such little code.

Having mirrored this library's structure, I noticed that I could have my own display area dimension, just like this library has an area dimension.

namespace dimension {

    using Display_area = units::dimension_pow<Display_length, std::ratio<2>>;

} // namespace dimension

How might this be useful? Well, the immediate plan for my game is to be tile-based. This means that I'll want units that represent tiles. Maybe there is a better concept than "area" out there, because it doesn't align with the fact that tiles represent a fixed size (for example, 32x32 tiles represent an area of 32 width of pixels by 32 height of pixels. That's 1024 px2. But the concept of area doesn't embed the knowledge of a fixed width and height.) Anyways, this is how I represented it in code.

namespace display_area {

    // Represents a tiled area of pixels.
    template <int W, int H = W>
    using Tiles = units::unit<
        units::unit_conversion<std::ratio<W * H>, dimension::Display_area>,
        int>;

} // namespace display_area

using display_area::Tiles;

static_assert(Tiles<32>{1} == 32_px * 32_px);

1024 px2 isn't obviously useful to me, so I embed the width and height in non-type template arguments. Now the logic for drawing the game's world could, for example, use the W and H to know the size of the area of pixels to draw. In C++20 I could even use strong types for W and H.

I found it amazing that the library could let me define these expressive domain units so succinctly. This got me enamoured with the library, because I know that when you can express things clearly in your domain, you can come up with things that would otherwise be hard to come up with (I've read some examples of this, but I don't have anything at hand).

So I took some time appreciating the beautiful code that this library allowed me to write, and thought further. Issue #103, where this, units library's author @nholthaus announced the coming of user-defined dimensions, suggests the need for units with run-time conversions. That issue shows an example of its opener's need, and one might also use that to define monetary units (there are concerns regarding the use of floating-point as their representation. The coming support for mixed underlying types might alleviate that (#126). Monetary units would be well-supported if the library eventually relaxes to allowing arithmetic-like underlying types. Then one could use a more appropriate underlying type, like fixed_point<int, -2, 10>).

On the same thread, pixel is quite a contested term. But one can conventionally adopt a definition of pixel to mean "the smallest addressable element in an all points addressable display device" (Wikipedia) "that contribute to the displayed or sensed color when viewed at a distance" (Wikipedia). With this definition, it is possible to define units that can represent other meanings of pixels. For example, a subpixel may be represented by the unit

using Subpixel = units::unit<
    units::unit_conversion<std::ratio<1, 3>, dimension::Display_length>, int>;

This is an expected functionality of units libraries. That's why we have (sub)multiples of length::meter_t, like length::millimeter_t. Where this has ties with run-time unit conversions is the fact that, if the library ever supports them, it would allow expressing a larger range of use cases. For example, if it was possible to read a PPI or DPI value, one could define units that map from the dimension of display length to the SI dimension of length. These units could be used in applications for printing devices that map pixels to actual sheet sizes, or perhaps in those that accommodate themselves when the DPI is too high. That's just to give an example. I don't think my actual use case for the game will need that, but is something that I thought of while contemplating the code I wrote.

@nholthaus
Copy link
Owner Author

nholthaus commented May 25, 2018

sort of off topic but wouldn't display length be a function of the isometric viewing angle and distance? Or is the game view 90 degrees top-down?

@JohelEGP
Copy link
Contributor

No, that'd be other thing. Display length is about the displayed image, which is independent of what is being displayed, and the viewing angle it pretends to imitate.

Or is the game view 90 degrees top-down?

Still independent, but more closely matches the concept. That's indeed how my game would look like:
Game map

@JohelEGP
Copy link
Contributor

JohelEGP commented Sep 8, 2018

The title should read "show off."

@nholthaus nholthaus changed the title Show of the power of the new variadic dimensions system Show off the power of the new variadic dimensions system Sep 8, 2018
@nholthaus
Copy link
Owner Author

@JohelEGP This is a great example of the use of user-defined dimensions. I'd be happy to write it up, unless you prefer to do so yourself.

Also, did you ever release your game? I'd rather play this than Cyberpunk 2077.

@JohelEGP
Copy link
Contributor

JohelEGP commented Oct 6, 2020

@JohelEGP This is a great example of the use of user-defined dimensions. I'd be happy to write it up, unless you prefer to do so yourself.

Where should it go?

Also, did you ever release your game? I'd rather play this than Cyberpunk 2077.

No. I'm still building the game engine.

@nholthaus
Copy link
Owner Author

nholthaus commented Oct 7, 2020

At the top of the README there's a new in 2.3.1. There's going to have to be a big headline sectionnew in 3.0.0 since we're changing C++ standards, compilers, ABI, etc. The organization of the README can be finessed later, but I want content that motivates the 3.0 changes to be featured prominently, and pixels is a great the best example.

@StefanoD
Copy link

@JohelEGP How can I access to W and H after declaring a Tiles in your Tile example code? I don't see any member variables

@JohelEGP
Copy link
Contributor

You can't get their individual values from a specialization of Tiles since they've become one, as W * H, in the aliased type.

@StefanoD
Copy link

@JohelEGP I thought so, thx.

@Lecrapouille
Copy link

@JohelEGP @nholthaus Could be nice to add pixel type and its conversion to meters. Of course, pixel are not yet recognize by the SI consortium ^^ but for top-down simulation application this can be interesting.

I'm doing a 2D car simulator (not yet public) using SFML library https://www.sfml-dev.org When I click on the GUI it returns to me the world coordinates (meter) and when I have to rendering trajectory I have to convert to pixel. SFML does not offer Pixel type (it is int), but its sf::Vector2<T> (T = float, double, int) is compatible with sf::Vector2<Meter>. SFML is also offering function mapCoordsToPixel and mapPixelToCoords through the SFML renderer's current view (which I guess will return its transfer matrix).

If units wants to convert automatically, pixel type to meter_t, this seems to me complicated because we have to pass the transfer matrix which has a dynamic state since the user change resize the windows, do zoom ...

@JohelEGP
Copy link
Contributor

If units wants to convert automatically, pixel type to meter_t, this seems to me complicated because we have to pass the transfer matrix which has a dynamic state since the user change resize the windows, do zoom ...

Yeah. Pixel can measure a virtual distance, so conversion to an actual, physical distance (or a simulation thereof) is application-dependent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants