Blender V5.0
screen_geometry.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include "BLI_listbase.h"
14#include "BLI_math_vector.h"
15#include "BLI_rect.h"
16
17#include "BKE_screen.hh"
18
19#include "DNA_screen_types.h"
21
22#include "ED_screen.hh"
23
24#include "MEM_guardedalloc.h"
25
26#include "WM_api.hh"
27
28#include "screen_intern.hh"
29
31{
32 return area->v2->vec.y - area->v1->vec.y + 1;
33}
35{
36 return area->v4->vec.x - area->v1->vec.x + 1;
37}
38
40{
41 ScrVert *sv = MEM_callocN<ScrVert>("addscrvert");
42 sv->vec.x = x;
43 sv->vec.y = y;
44
45 BLI_addtail(&area_map->vertbase, sv);
46 return sv;
47}
48ScrVert *screen_geom_vertex_add(bScreen *screen, short x, short y)
49{
51}
52
54{
55 ScrEdge *se = MEM_callocN<ScrEdge>("addscredge");
56
58 se->v1 = v1;
59 se->v2 = v2;
60
61 BLI_addtail(&area_map->edgebase, se);
62 return se;
63}
68
70{
71 return (se->v1->vec.y == se->v2->vec.y);
72}
73
75 const ScrAreaMap *area_map, const rcti *bounds_rect, const int mx, const int my, int safety)
76{
77 CLAMP_MIN(safety, 2);
78
79 LISTBASE_FOREACH (ScrEdge *, se, &area_map->edgebase) {
81 if ((se->v1->vec.y > bounds_rect->ymin) && (se->v1->vec.y < (bounds_rect->ymax - 1))) {
82 short min, max;
83 min = std::min(se->v1->vec.x, se->v2->vec.x);
84 max = std::max(se->v1->vec.x, se->v2->vec.x);
85
86 if (abs(my - se->v1->vec.y) <= safety && mx >= min && mx <= max) {
87 return se;
88 }
89 }
90 }
91 else {
92 if ((se->v1->vec.x > bounds_rect->xmin) && (se->v1->vec.x < (bounds_rect->xmax - 1))) {
93 short min, max;
94 min = std::min(se->v1->vec.y, se->v2->vec.y);
95 max = std::max(se->v1->vec.y, se->v2->vec.y);
96
97 if (abs(mx - se->v1->vec.x) <= safety && my >= min && my <= max) {
98 return se;
99 }
100 }
101 }
102 }
103
104 return nullptr;
105}
106
108 const bScreen *screen,
109 const int mx,
110 const int my)
111{
112 if (U.app_flag & USER_APP_LOCK_EDGE_RESIZE) {
113 return nullptr;
114 }
115
116 /* Use layout size (screen excluding global areas) for screen-layout area edges */
117 rcti screen_rect;
118 WM_window_screen_rect_calc(win, &screen_rect);
120 AREAMAP_FROM_SCREEN(screen), &screen_rect, mx, my, BORDERPADDING);
121
122 if (!se) {
123 /* Use entire window size (screen including global areas) for global area edges */
124 rcti win_rect;
125 WM_window_rect_calc(win, &win_rect);
127 &win->global_areas, &win_rect, mx, my, int(BORDERPADDING_GLOBAL));
128 }
129 return se;
130}
131
137 const bScreen *screen,
138 const rcti *screen_rect)
139{
140
141 const int screen_size_x = BLI_rcti_size_x(screen_rect);
142 const int screen_size_y = BLI_rcti_size_y(screen_rect);
143 bool needs_another_pass = false;
144
145 /* calculate size */
146 float min[2] = {20000.0f, 20000.0f};
147 float max[2] = {0.0f, 0.0f};
148
149 LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
150 const float fv[2] = {float(sv->vec.x), float(sv->vec.y)};
151 minmax_v2v2_v2(min, max, fv);
152 }
153
154 int screen_size_x_prev = (max[0] - min[0]) + 1;
155 int screen_size_y_prev = (max[1] - min[1]) + 1;
156
157 if (screen_size_x_prev != screen_size_x || screen_size_y_prev != screen_size_y) {
158 const float facx = (float(screen_size_x) - 1) / (float(screen_size_x_prev) - 1);
159 const float facy = (float(screen_size_y) - 1) / (float(screen_size_y_prev) - 1);
160
161 /* make sure it fits! */
162 LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
163 sv->vec.x = screen_rect->xmin + round_fl_to_short((sv->vec.x - min[0]) * facx);
164 CLAMP(sv->vec.x, screen_rect->xmin, screen_rect->xmax - 1);
165
166 sv->vec.y = screen_rect->ymin + round_fl_to_short((sv->vec.y - min[1]) * facy);
167 CLAMP(sv->vec.y, screen_rect->ymin, screen_rect->ymax - 1);
168 }
169
170 /* test for collapsed areas. This could happen in some blender version... */
171 /* ton: removed option now, it needs Context... */
172
173 if (facy > 1) {
174 /* Keep timeline small in video edit workspace. */
175 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
176 const int border_width = int(ceil(float(U.border_width) * UI_SCALE_FAC));
177 int min = ED_area_headersize() + border_width;
178 if (area->v1->vec.y > screen_rect->ymin) {
179 min += border_width;
180 }
181 if (area->spacetype == SPACE_ACTION && area->v1->vec.y == screen_rect->ymin &&
182 screen_geom_area_height(area) <= int(min * 1.5f))
183 {
184 ScrEdge *se = BKE_screen_find_edge(screen, area->v2, area->v3);
185 if (se) {
186 const int yval = area->v1->vec.y + min - 1;
187
189
190 /* all selected vertices get the right offset */
191 LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
192 /* if is a collapsed area */
193 if (!ELEM(sv, area->v1, area->v4)) {
194 if (sv->flag) {
195 sv->vec.y = yval;
196 /* Changed size of a area. Run another pass to ensure everything still fits. */
197 needs_another_pass = true;
198 }
199 }
200 }
201 }
202 }
203 }
204 }
205
206 /* Make each window at least ED_area_headersize() high. This
207 * should be done whether we are increasing or decreasing the
208 * vertical size since this is called on file load, not just
209 * during resize operations. */
210 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
211 const int border_width = int(ceil(float(U.border_width) * UI_SCALE_FAC));
212 int min = ED_area_headersize() + border_width + border_width - U.pixelsize;
213 if (area->v3->vec.y >= (screen_rect->ymax - 1)) {
214 /* Area aligned to top screen edge. */
215 min = ED_area_headersize() + border_width;
216 }
217 else if (area->v4->vec.y <= (screen_rect->ymin + 1)) {
218 /* Area aligned to bottom screen edge. */
219 min = ED_area_headersize() + border_width + 1;
220 }
221
222 const int height = screen_geom_area_height(area);
223 if (height < min) {
224 /* lower edge */
225 ScrEdge *se = BKE_screen_find_edge(screen, area->v4, area->v1);
226 if (se && area->v1 != area->v2) {
227 const int yval = area->v2->vec.y - min;
228
230
231 /* all selected vertices get the right offset */
232 LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) {
233 /* if is not a collapsed area */
234 if (!ELEM(sv, area->v2, area->v3)) {
235 if (sv->flag) {
236 sv->vec.y = yval;
237 /* Changed size of a area. Run another pass to ensure everything still fits. */
238 needs_another_pass = true;
239 }
240 }
241 }
242 }
243 }
244 }
245 }
246
247 return needs_another_pass;
248}
249
251{
252 rcti window_rect, screen_rect;
253 WM_window_rect_calc(win, &window_rect);
254 WM_window_screen_rect_calc(win, &screen_rect);
255
256 bool needs_another_pass;
257 int max_passes_left = 10; /* Avoids endless loop. Number is rather arbitrary. */
258 do {
259 needs_another_pass = screen_geom_vertices_scale_pass(win, screen, &screen_rect);
260 max_passes_left--;
261 } while (needs_another_pass && (max_passes_left > 0));
262
263 /* Global areas have a fixed size that only changes with the DPI.
264 * Here we ensure that exactly this size is set. */
266 if (area->global->flag & GLOBAL_AREA_IS_HIDDEN) {
267 continue;
268 }
269
270 int height = ED_area_global_size_y(area) - 1;
271
272 if (area->v1->vec.y > window_rect.ymin) {
273 height += U.pixelsize;
274 }
275 if (area->v2->vec.y < (window_rect.ymax - 1)) {
276 height += U.pixelsize;
277 }
278
279 /* width */
280 area->v1->vec.x = area->v2->vec.x = window_rect.xmin;
281 area->v3->vec.x = area->v4->vec.x = window_rect.xmax - 1;
282 /* height */
283 area->v1->vec.y = area->v4->vec.y = window_rect.ymin;
284 area->v2->vec.y = area->v3->vec.y = window_rect.ymax - 1;
285
286 switch (area->global->align) {
288 area->v1->vec.y = area->v4->vec.y = area->v2->vec.y - height;
289 break;
291 area->v2->vec.y = area->v3->vec.y = area->v1->vec.y + height;
292 break;
293 }
294 }
295}
296
298 const rcti *window_rect,
299 const eScreenAxis dir_axis,
300 float fac)
301{
302 const int cur_area_width = screen_geom_area_width(area);
303 const int cur_area_height = screen_geom_area_height(area);
304 const short area_min_x = AREAMINX * UI_SCALE_FAC;
305 const short area_min_y = ED_area_headersize();
306
307 /* area big enough? */
308 if (dir_axis == SCREEN_AXIS_V) {
309 if (cur_area_width <= 2 * area_min_x) {
310 return 0;
311 }
312 }
313 else if (dir_axis == SCREEN_AXIS_H) {
314 if (cur_area_height <= 2 * area_min_y) {
315 return 0;
316 }
317 }
318
319 /* to be sure */
320 CLAMP(fac, 0.0f, 1.0f);
321
322 if (dir_axis == SCREEN_AXIS_H) {
323 short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height);
324
325 int area_min = area_min_y;
326
327 if (area->v1->vec.y > window_rect->ymin) {
328 area_min += U.pixelsize;
329 }
330 if (area->v2->vec.y < (window_rect->ymax - 1)) {
331 area_min += U.pixelsize;
332 }
333
334 if (y - area->v1->vec.y < area_min) {
335 y = area->v1->vec.y + area_min;
336 }
337 else if (area->v2->vec.y - y < area_min) {
338 y = area->v2->vec.y - area_min;
339 }
340
341 return y;
342 }
343
344 short x = area->v1->vec.x + round_fl_to_short(fac * cur_area_width);
345
346 int area_min = area_min_x;
347
348 if (area->v1->vec.x > window_rect->xmin) {
349 area_min += U.pixelsize;
350 }
351 if (area->v4->vec.x < (window_rect->xmax - 1)) {
352 area_min += U.pixelsize;
353 }
354
355 if (x - area->v1->vec.x < area_min) {
356 x = area->v1->vec.x + area_min;
357 }
358 else if (area->v4->vec.x - x < area_min) {
359 x = area->v4->vec.x - area_min;
360 }
361
362 return x;
363}
364
366{
368
369 /* 'dir_axis' is the direction of EDGE */
370 eScreenAxis dir_axis;
371 if (edge->v1->vec.x == edge->v2->vec.x) {
372 dir_axis = SCREEN_AXIS_V;
373 }
374 else {
375 dir_axis = SCREEN_AXIS_H;
376 }
377
378 ED_screen_verts_iter(win, screen, sv)
379 {
380 sv->flag = 0;
381 }
382
383 edge->v1->flag = 1;
384 edge->v2->flag = 1;
385
386 /* select connected, only in the right direction */
387 bool oneselected = true;
388 while (oneselected) {
389 oneselected = false;
390 LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
391 if (se->v1->flag + se->v2->flag == 1) {
392 if (dir_axis == SCREEN_AXIS_H) {
393 if (se->v1->vec.y == se->v2->vec.y) {
394 se->v1->flag = se->v2->flag = 1;
395 oneselected = true;
396 }
397 }
398 else if (dir_axis == SCREEN_AXIS_V) {
399 if (se->v1->vec.x == se->v2->vec.x) {
400 se->v1->flag = se->v2->flag = 1;
401 oneselected = true;
402 }
403 }
404 }
405 }
406 }
407}
void BKE_screen_sort_scrvert(ScrVert **v1, ScrVert **v2)
Definition screen.cc:688
ScrEdge * BKE_screen_find_edge(const bScreen *screen, ScrVert *v1, ScrVert *v2)
Definition screen.cc:676
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE short round_fl_to_short(float a)
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
#define CLAMP(a, b, c)
#define ELEM(...)
#define CLAMP_MIN(a, b)
#define AREAMAP_FROM_SCREEN(screen)
#define AREAMINX
@ GLOBAL_AREA_IS_HIDDEN
@ GLOBAL_AREA_ALIGN_BOTTOM
@ GLOBAL_AREA_ALIGN_TOP
@ SPACE_ACTION
#define UI_SCALE_FAC
@ USER_APP_LOCK_EDGE_RESIZE
int ED_area_global_size_y(const ScrArea *area)
Definition area.cc:3967
#define ED_screen_verts_iter(win, screen, vert_name)
Definition ED_screen.hh:299
int ED_area_headersize()
Definition area.cc:3956
Read Guarded memory(de)allocation.
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
nullptr float
#define abs
#define ceil
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ScrVert * screen_geom_vertex_add(bScreen *screen, short x, short y)
ScrEdge * screen_geom_edge_add_ex(ScrAreaMap *area_map, ScrVert *v1, ScrVert *v2)
ScrVert * screen_geom_vertex_add_ex(ScrAreaMap *area_map, short x, short y)
ScrEdge * screen_geom_find_active_scredge(const wmWindow *win, const bScreen *screen, const int mx, const int my)
ScrEdge * screen_geom_edge_add(bScreen *screen, ScrVert *v1, ScrVert *v2)
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
bool screen_geom_edge_is_horizontal(ScrEdge *se)
static bool screen_geom_vertices_scale_pass(const wmWindow *win, const bScreen *screen, const rcti *screen_rect)
int screen_geom_area_width(const ScrArea *area)
ScrEdge * screen_geom_area_map_find_active_scredge(const ScrAreaMap *area_map, const rcti *bounds_rect, const int mx, const int my, int safety)
int screen_geom_area_height(const ScrArea *area)
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
Main screen-layout calculation function.
short screen_geom_find_area_split_point(const ScrArea *area, const rcti *window_rect, const eScreenAxis dir_axis, float fac)
#define BORDERPADDING
eScreenAxis
@ SCREEN_AXIS_V
@ SCREEN_AXIS_H
#define BORDERPADDING_GLOBAL
#define min(a, b)
Definition sort.cc:36
ListBase vertbase
ListBase edgebase
ListBase areabase
ScrVert * v2
ScrVert * v1
ScrVert * v4
ScrVert * v1
ScrVert * v2
ListBase edgebase
ListBase vertbase
ListBase areabase
int ymin
int ymax
int xmin
int xmax
short y
short x
ScrAreaMap global_areas
max
Definition text_draw.cc:251
void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
bScreen * WM_window_get_active_screen(const wmWindow *win)