@@ -29,30 +29,99 @@ SOFTWARE.
2929
3030namespace sago {
3131
32- class SagoLogicalResize
33- {
32+ /* *
33+ * @brief Provides coordinate transformation between logical and physical screen coordinates.
34+ *
35+ * This class helps create resolution-independent layouts by defining coordinates in a fixed
36+ * "logical" resolution and automatically converting them to the actual "physical" screen size.
37+ * It maintains aspect ratio by adding letterboxing/pillarboxing margins when needed.
38+ *
39+ * Key concepts:
40+ * - Logical coordinates: Fixed coordinate system you design for (e.g., 1920x1080)
41+ * - Physical coordinates: Actual screen/window size (e.g., 1280x720, 3840x2160, etc.)
42+ * - Scale factor: Calculated to fit logical size into physical size while preserving aspect ratio
43+ * - Margins: Black bars added to maintain aspect ratio (letterboxing/pillarboxing)
44+ * - Tile alignment: As long as tiles are resized with LogicalToPhysical, they will align correctly. No gaps or overlaps.
45+ *
46+ * Example usage:
47+ * @code
48+ * // Design your UI for 1920x1080
49+ * SagoLogicalResize resize(1920, 1080);
50+ * resize.SetPhysicalSize(window_width, window_height);
51+ *
52+ * // Draw at logical position (100, 100)
53+ * int phys_x, phys_y;
54+ * resize.LogicalToPhysical(100, 100, phys_x, phys_y);
55+ * DrawSprite(renderer, phys_x, phys_y);
56+ *
57+ * // Handle mouse input
58+ * int log_x, log_y;
59+ * resize.PhysicalToLogical(mouse_x, mouse_y, log_x, log_y);
60+ * if (log_x > 100 && log_x < 200) { ... }
61+ * @endcode
62+ */
63+ class SagoLogicalResize {
3464public:
35- SagoLogicalResize () : logical_width_(1 ), logical_height_(1 ), physical_width_(1 ), physical_height_(1 ) { SetScaleFactor (); }
65+ /* *
66+ * @brief Default constructor with minimal 1x1 logical size.
67+ */
68+ SagoLogicalResize () : logical_width_(1 ), logical_height_(1 ), physical_width_(1 ), physical_height_(1 ) {
69+ SetScaleFactor ();
70+ }
71+
72+ /* *
73+ * @brief Constructor with specified logical resolution.
74+ * @param logical_width The width of the logical coordinate system (minimum 1)
75+ * @param logical_height The height of the logical coordinate system (minimum 1)
76+ */
3677 SagoLogicalResize (int logical_width, int logical_height)
37- : logical_width_(std::max(1 , logical_width)), logical_height_(std::max(1 , logical_height)), physical_width_(1 ), physical_height_(1 ) { SetScaleFactor (); }
78+ : logical_width_(std::max(1 , logical_width)), logical_height_(std::max(1 , logical_height)), physical_width_(1 ), physical_height_(1 ) {
79+ SetScaleFactor ();
80+ }
3881
39- void SetPhysicalSize (int physical_width, int physical_height)
40- {
82+ /* *
83+ * @brief Updates the physical (actual) screen size and recalculates scaling.
84+ *
85+ * Call this when the window is resized or when initializing the physical display size.
86+ * This recalculates the scale factor and margins to fit the logical size into the physical size.
87+ *
88+ * @param physical_width The actual width of the screen/window (minimum 1)
89+ * @param physical_height The actual height of the screen/window (minimum 1)
90+ */
91+ void SetPhysicalSize (int physical_width, int physical_height) {
4192 // Physical size must be at least 1. Less than 1 is not drawn anyway and it prevents division by zero.
4293 physical_width_ = std::max (1 , physical_width);
4394 physical_height_ = std::max (1 , physical_height);
4495 SetScaleFactor ();
4596 }
4697
47- void LogicalToPhysical (int logical_x, int logical_y, int &physical_x, int &physical_y) const
48- {
98+ /* *
99+ * @brief Converts logical coordinates to physical screen coordinates.
100+ *
101+ * This applies the scale factor and adds the appropriate margins.
102+ * Use this to convert positions where you want to draw elements on screen.
103+ *
104+ * @param logical_x The x coordinate in logical space
105+ * @param logical_y The y coordinate in logical space
106+ * @param physical_x Output: the x coordinate in physical screen space
107+ * @param physical_y Output: the y coordinate in physical screen space
108+ */
109+ void LogicalToPhysical (int logical_x, int logical_y, int & physical_x, int & physical_y) const {
49110 physical_x = logical_x;
50111 physical_y = logical_y;
51112 LogicalToPhysical (&physical_x, &physical_y);
52113 }
53114
54- void LogicalToPhysical (int *x, int *y) const
55- {
115+ /* *
116+ * @brief Converts logical coordinates to physical screen coordinates (pointer version).
117+ *
118+ * This applies the scale factor and adds the appropriate margins.
119+ * Null pointers are safely ignored.
120+ *
121+ * @param x Pointer to x coordinate (will be converted in place), can be null
122+ * @param y Pointer to y coordinate (will be converted in place), can be null
123+ */
124+ void LogicalToPhysical (int * x, int * y) const {
56125 if (x) {
57126 *x = *x * scale_factor_ + left_margin_;
58127 }
@@ -61,46 +130,90 @@ class SagoLogicalResize
61130 }
62131 }
63132
64- void LogicalToPhysical (SDL_Rect &inout) const
65- {
133+ /* *
134+ * @brief Converts a logical rectangle to physical screen coordinates.
135+ *
136+ * This converts both the position AND size of a rectangle. The rectangle
137+ * is transformed in place. Use this for converting areas/regions rather than
138+ * just single points.
139+ *
140+ * Note: This properly handles the size conversion by converting the bottom-right
141+ * corner and calculating the new width/height from the difference.
142+ *
143+ * @param inout The rectangle in logical coordinates (input), converted to physical coordinates (output)
144+ */
145+ void LogicalToPhysical (SDL_Rect& inout) const {
66146 SDL_Rect input = inout;
67147 LogicalToPhysical (&inout.x , &inout.y );
68148 LogicalToPhysical (input.x + input.w + 1 , input.y + input.h + 1 , inout.w , inout.h );
69149 inout.w -= inout.x - 1 ;
70150 inout.h -= inout.y - 1 ;
71151 }
72152
73- void PhysicalToLogical (int physical_x, int physical_y, int &logical_x, int &logical_y) const
74- {
153+ /* *
154+ * @brief Converts physical screen coordinates to logical coordinates.
155+ *
156+ * Use this to convert mouse input or other physical positions back to your
157+ * logical coordinate system for hit detection and input handling.
158+ * This removes margins and applies inverse scaling.
159+ *
160+ * @param physical_x The x coordinate in physical screen space
161+ * @param physical_y The y coordinate in physical screen space
162+ * @param logical_x Output: the x coordinate in logical space
163+ * @param logical_y Output: the y coordinate in logical space
164+ */
165+ void PhysicalToLogical (int physical_x, int physical_y, int & logical_x, int & logical_y) const {
75166 logical_x = (physical_x - left_margin_) / scale_factor_;
76167 logical_y = (physical_y - top_margin_) / scale_factor_;
77168 }
78169
79- int GetTopMargin () const
80- {
170+ /* *
171+ * @brief Gets the top margin (letterboxing/pillarboxing offset).
172+ *
173+ * This is the black bar size at the top when the aspect ratio requires vertical margins.
174+ * Useful for debugging or custom rendering that needs to know the drawable area.
175+ *
176+ * @return The top margin in physical pixels
177+ */
178+ int GetTopMargin () const {
81179 return top_margin_;
82180 }
83181
84- int GetLeftMargin () const
85- {
182+ /* *
183+ * @brief Gets the left margin (letterboxing/pillarboxing offset).
184+ *
185+ * This is the black bar size on the left when the aspect ratio requires horizontal margins.
186+ * Useful for debugging or custom rendering that needs to know the drawable area.
187+ *
188+ * @return The left margin in physical pixels
189+ */
190+ int GetLeftMargin () const {
86191 return left_margin_;
87192 }
88193
89194private:
90- void SetScaleFactor ()
91- {
195+ /* *
196+ * @brief Calculates the scale factor and margins based on current logical and physical sizes.
197+ *
198+ * The scale factor is chosen to fit the logical size into the physical size while
199+ * maintaining aspect ratio. The margins center the content on screen.
200+ *
201+ * Scale factor = min(physical_width/logical_width, physical_height/logical_height)
202+ * This ensures the content fits in both dimensions without distortion.
203+ */
204+ void SetScaleFactor () {
92205 scale_factor_ = std::min (physical_width_ / logical_width_, physical_height_ / logical_height_);
93206 left_margin_ = (physical_width_ - logical_width_ * scale_factor_) / 2 ;
94207 top_margin_ = (physical_height_ - logical_height_ * scale_factor_) / 2 ;
95208 }
96209
97- double scale_factor_;
98- int top_margin_;
99- int left_margin_;
100- double logical_width_;
101- double logical_height_;
102- double physical_width_;
103- double physical_height_;
210+ double scale_factor_; // /< Multiplier to convert logical size to physical size
211+ int top_margin_; // /< Vertical offset for centering (letterboxing)
212+ int left_margin_; // /< Horizontal offset for centering (pillarboxing)
213+ double logical_width_; // /< Width of the logical coordinate system
214+ double logical_height_; // /< Height of the logical coordinate system
215+ double physical_width_; // /< Actual width of the screen/window
216+ double physical_height_; // /< Actual height of the screen/window
104217};
105218
106219} // namespace sago
0 commit comments