Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezScreen is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : namespace ez {
30 : : namespace screen {
31 : :
32 : : // --- not blocking floor is not float or double ---
33 : : template <typename T>
34 : 2 : inline typename std::enable_if<std::is_floating_point<T>::value, T>::type floor_nb(T v) {
35 : 2 : return floor<T>(v);
36 : 2 : }
37 : :
38 : : template <typename T>
39 : 1 : inline typename std::enable_if<!std::is_floating_point<T>::value, T>::type floor_nb(T v) {
40 : 1 : return v;
41 : 1 : }
42 : :
43 : : // --- not blocking floor vec4 is not float or double ---
44 : : template <typename T>
45 : 6 : inline typename std::enable_if<std::is_floating_point<T>::value, vec4<T>>::type floor_nb(const vec4<T>& v) {
46 : 6 : return vec4<T>(static_cast<T>(std::floor(v.x)), static_cast<T>(std::floor(v.y)), static_cast<T>(std::floor(v.z)), static_cast<T>(std::floor(v.w)));
47 : 6 : }
48 : :
49 : : template <typename T>
50 : 1 : inline typename std::enable_if<!std::is_floating_point<T>::value, vec4<T>>::type floor_nb(const vec4<T>& v) {
51 : 1 : return v;
52 : 1 : }
53 : :
54 : : template <typename T>
55 : 3 : inline vec4<T> getScreenRectWithSize(vec2<T> vItemSize, vec2<T> vMaxSize) {
56 : 3 : vec4<T> rc;
57 : :
58 : 3 : fvec2 visibleSize = fvec2((float)vMaxSize.x, (float)vMaxSize.y);
59 [ + - ][ + - ]: 3 : if (visibleSize.x > 0.0f && visibleSize.y > 0.0f) {
60 : 3 : fvec2 visibleOrigin;
61 : 3 : const fvec2 texSize = fvec2((float)vItemSize.x, (float)vItemSize.y);
62 : :
63 : : // float visibleRatio = visibleSize.x / visibleSize.y;
64 : 3 : const float refRatio = texSize.x / texSize.y;
65 : :
66 : 3 : float newX = visibleSize.y * refRatio;
67 : 3 : float newY = visibleSize.x / refRatio;
68 : :
69 [ + + ]: 3 : if (newX < visibleSize.x) {
70 : : // pos
71 : 1 : rc.x = (T)(visibleOrigin.x + (visibleSize.x - newX) * 0.5f);
72 : 1 : rc.y = (T)visibleOrigin.y;
73 : :
74 : : // size
75 : 1 : rc.z = (T)newX;
76 : 1 : rc.w = (T)visibleSize.y;
77 : 2 : } else {
78 : : // pos
79 : 2 : rc.x = (T)visibleOrigin.x;
80 : 2 : rc.y = (T)(visibleOrigin.y + (visibleSize.y - newY) * 0.5f);
81 : :
82 : : // size
83 : 2 : rc.z = (T)visibleSize.x;
84 : 2 : rc.w = (T)newY;
85 : 2 : }
86 : :
87 : 3 : rc = floor_nb<T>(rc);
88 : :
89 : : /*const float newRatio = (float)rc.w / (float)rc.h;
90 : : if (vLogError)
91 : : if (IS_FLOAT_DIFFERENT(newRatio, refRatio))
92 : : printf("GetScreenRectWithRatio : the new ratio is not the same as the ref ratio\n");*/
93 : 3 : }
94 : :
95 : 3 : return rc;
96 : 3 : }
97 : :
98 : : template <typename T>
99 : 2 : inline vec4<T> getScreenRectWithRatio(float vRatio, vec2<T> vMaxSize) {
100 : 2 : vec4<T> rc;
101 : :
102 : 2 : fvec2 visibleSize = fvec2((float)vMaxSize.x, (float)vMaxSize.y);
103 [ + - ][ + - ]: 2 : if (visibleSize.x > 0.0f && visibleSize.y > 0.0f) {
104 : 2 : fvec2 visibleOrigin;
105 : :
106 : 2 : const float refRatio = vRatio;
107 : :
108 : 2 : float newX = visibleSize.y * refRatio;
109 : 2 : float newY = visibleSize.x / refRatio;
110 : :
111 [ + + ]: 2 : if (newX < visibleSize.x) {
112 : : // pos
113 : 1 : rc.x = (T)(visibleOrigin.x + (visibleSize.x - newX) * 0.5f);
114 : 1 : rc.y = (T)visibleOrigin.y;
115 : :
116 : : // size
117 : 1 : rc.z = (T)newX;
118 : 1 : rc.w = (T)visibleSize.y;
119 : 1 : } else {
120 : : // pos
121 : 1 : rc.x = (T)visibleOrigin.x;
122 : 1 : rc.y = (T)(visibleOrigin.y + (visibleSize.y - newY) * 0.5f);
123 : :
124 : : // size
125 : 1 : rc.z = (T)visibleSize.x;
126 : 1 : rc.w = (T)newY;
127 : 1 : }
128 : :
129 : 2 : rc = floor_nb<T>(rc);
130 : :
131 : : /*const float newRatio = (float)rc.w / (float)rc.h;
132 : : if (vLogError)
133 : : if (IS_FLOAT_DIFFERENT(newRatio, refRatio))
134 : : printf("GetScreenRectWithRatio : the new ratio is not the same as the ref ratio\n");*/
135 : 2 : }
136 : :
137 : 2 : return rc;
138 : 2 : }
139 : :
140 : : inline fvec2 screenToWorld_centered(
141 : : const fvec2& mousePx,
142 : : const fvec2& viewportSize,
143 : : const fvec2& worldCenter,
144 : : float sPx,
145 : : bool yDown // true si Y écran descend
146 : 3 : ) {
147 : 3 : const float VW = viewportSize.x, VH = viewportSize.y;
148 : 3 : const float dx = mousePx.x - 0.5f * VW;
149 [ + + ]: 3 : const float dy = yDown ? (mousePx.y - 0.5f * VH) : (0.5f * VH - mousePx.y);
150 : 3 : return {worldCenter.x + dx / sPx, worldCenter.y + dy / sPx};
151 : 3 : }
152 : :
153 : : inline void centeredZoom(
154 : : float factor, // >1 zoom-in, <1 zoom-out
155 : : const fvec2& mousePx,
156 : : const fvec2& viewportSize,
157 : : bool yDown,
158 : : float sFitPx, // pixels/unit, issu de ton fit
159 : : float& zoom, // IN/OUT
160 : : fvec2& worldCenter // IN/OUT
161 : 1 : ) {
162 [ - + ][ - + ]: 1 : if (factor <= 0.0f || sFitPx <= 0.0f)
163 : 0 : return;
164 : :
165 : 1 : const float VW = viewportSize.x, VH = viewportSize.y;
166 : 1 : const float sPx_before = zoom * sFitPx;
167 : :
168 : : // delta écran (centre -> souris), signé selon Y-up/Y-down
169 : 1 : const float dx = mousePx.x - 0.5f * VW;
170 [ + - ]: 1 : const float dy = yDown ? (mousePx.y - 0.5f * VH) : (0.5f * VH - mousePx.y);
171 : :
172 : : // point monde sous la souris AVANT zoom
173 : 1 : const fvec2 worldUnderMouse = {worldCenter.x + dx / sPx_before, worldCenter.y + dy / sPx_before};
174 : :
175 : : // nouveau zoom
176 : 1 : zoom *= factor;
177 : 1 : const float sPx_after = zoom * sFitPx;
178 : :
179 : : // ajuste le centre pour garder worldUnderMouse sous la souris
180 : 1 : worldCenter = {worldUnderMouse.x - dx / sPx_after, worldUnderMouse.y - dy / sPx_after};
181 : 1 : }
182 : :
183 : : inline void zoomedTranslation(
184 : : const fvec2& dragPx, // delta souris en pixels
185 : : bool yDown,
186 : : float sFitPx,
187 : : float zoom,
188 : : fvec2& worldCenter // IN/OUT
189 : 1 : ) {
190 [ - + ][ - + ]: 1 : if (sFitPx <= 0.0f || zoom <= 0.0f)
191 : 0 : return;
192 : 1 : const float sPx = zoom * sFitPx;
193 : :
194 : : // déplacer le centre à l'opposé du drag (comme une carte)
195 : 1 : const float dx = dragPx.x;
196 [ + - ]: 1 : const float dy = yDown ? dragPx.y : -dragPx.y;
197 : :
198 : 1 : worldCenter.x -= dx / sPx;
199 : 1 : worldCenter.y -= dy / sPx;
200 : 1 : }
201 : :
202 : 1 : inline float computeFitScalePx(const fvec2& worldMin, const fvec2& worldMax, const fvec2& viewportSize) {
203 : 1 : const float W = worldMax.x - worldMin.x;
204 : 1 : const float H = worldMax.y - worldMin.y;
205 [ - + ][ - + ]: 1 : if (W <= 0.0f || H <= 0.0f || viewportSize.x <= 0.0f || viewportSize.y <= 0.0f)
[ - + ][ - + ]
206 : 0 : return 0.0f;
207 : 1 : const fvec4 rc = getScreenRectWithSize({W, H}, viewportSize); // {x,y,w,h}
208 : 1 : return rc.z / W; // == rc.w / H
209 : 1 : }
210 : :
211 : : // Fit "contain" d'un AABB monde dans un framebuffer en pixels.
212 : : // Mapping utilisé : screen = world * scale + originPx
213 : : inline void computeFitToContent(
214 : : const ez::fvec2& vWorldMin,
215 : : const ez::fvec2& vWorldMax,
216 : : const ez::fvec2& vFramebufferSizePx,
217 : : ez::fvec2& voOriginPx,
218 : : float& voScale,
219 : 1 : float& voInvScale) {
220 : 1 : const float W = vWorldMax.x - vWorldMin.x;
221 : 1 : const float H = vWorldMax.y - vWorldMin.y;
222 : 1 : const float VW = vFramebufferSizePx.x;
223 : 1 : const float VH = vFramebufferSizePx.y;
224 : :
225 [ - + ][ - + ]: 1 : if (W <= 0.0f || H <= 0.0f || VW <= 0.0f || VH <= 0.0f) {
[ - + ][ - + ]
226 : 0 : voOriginPx = {0.0f, 0.0f};
227 : 0 : voScale = 1.0f;
228 : 0 : voInvScale = 1.0f;
229 : 0 : return;
230 : 0 : }
231 : :
232 : 1 : const float sx = VW / W;
233 : 1 : const float sy = VH / H;
234 [ + - ]: 1 : const float s = (sx < sy) ? sx : sy;
235 : :
236 : 1 : const float padX = 0.5f * (VW - W * s);
237 : 1 : const float padY = 0.5f * (VH - H * s);
238 : :
239 : 1 : voOriginPx.x = padX - vWorldMin.x * s;
240 : 1 : voOriginPx.y = padY - vWorldMin.y * s;
241 : 1 : voScale = s;
242 [ + - ]: 1 : voInvScale = (s != 0.0f) ? (1.0f / s) : 0.0f;
243 : 1 : }
244 : :
245 : : } // namespace screen
246 : : } // namespace ez
|