Merge branch 'blender-v4.5-release'

This commit is contained in:
Sean Kim
2025-06-24 10:28:14 -07:00
10 changed files with 79 additions and 53 deletions

View File

@@ -1004,22 +1004,37 @@ static inline void sort_points(int2 &p1, int2 &p2)
}
}
/* Ensures that the x and y distance to from p1 to p2 is equal. The two points can be in any
* spacial relation to each other i.e. if p1 was top left, it remains top left. */
static inline void square_points(const int2 &p1, int2 &p2)
/* Clamps the point to the window bounds. */
static inline int2 clamp_point_to_window(const int2 &point, const wmWindow *window)
{
int2 delta = p2 - p1;
return {clamp_i(point.x, 0, window->sizex - 1), clamp_i(point.y, 0, window->sizey - 1)};
}
/* Ensures that the x and y distance to from p1 to p2 is equal and the resulting square remains
* fully within the window bounds. The two points can be in any spacial relation to each other i.e.
* if p1 was top left, it remains top left. */
static inline void square_points_clamp_to_window(const int2 &p1, int2 &p2, const wmWindow *window)
{
const int2 delta = p2 - p1;
/* Determine the drag direction for each axis. */
const int dir_x = (delta.x >= 0) ? 1 : -1;
const int dir_y = (delta.y >= 0) ? 1 : -1;
const int size_x = std::abs(delta.x);
const int size_y = std::abs(delta.y);
if (size_x < size_y) {
delta.x = std::copysignf(size_y, delta.x);
}
else if (size_y < size_x) {
delta.y = std::copysign(size_x, delta.y);
}
p2.x = p1.x + delta.x;
p2.y = p1.y + delta.y;
int square_size = std::max(size_x, size_y);
/* Compute maximum size that fits within window bounds in the drag direction. */
const int max_size_x = (dir_x > 0) ? window->sizex - p1.x - 1 : p1.x;
const int max_size_y = (dir_y > 0) ? window->sizey - p1.y - 1 : p1.y;
/* Clamp the square size so it does not exceed window bounds. */
square_size = std::min(square_size, std::min(max_size_x, max_size_y));
/* Update p2 to form a clamped square in the same direction as the drag. */
p2.x = p1.x + dir_x * square_size;
p2.y = p1.y + dir_y * square_size;
}
static void generate_previewimg_from_buffer(ID *id, const ImBuf *image_buffer)
@@ -1105,13 +1120,18 @@ static ImBuf *take_screenshot_crop(bContext *C, const rcti &crop_rect)
static wmOperatorStatus screenshot_preview_exec(bContext *C, wmOperator *op)
{
int2 p1, p2;
wmWindow *win = CTX_wm_window(C);
RNA_int_get_array(op->ptr, "p1", p1);
RNA_int_get_array(op->ptr, "p2", p2);
/* Clamp points to window bounds, so the screenshot area is always valid. */
p1 = clamp_point_to_window(p1, win);
p2 = clamp_point_to_window(p2, win);
/* Squaring has to happen before sorting so the area is squared from the point where
* dragging started. */
if (RNA_boolean_get(op->ptr, "force_square")) {
square_points(p1, p2);
square_points_clamp_to_window(p1, p2, win);
}
sort_points(p1, p2);
@@ -1207,9 +1227,16 @@ static void screenshot_preview_draw(const wmWindow *window, void *operator_data)
int2 p1 = data->p1;
int2 p2 = data->p2;
/* Clamp points to window bounds, so the screenshot area is always valid. */
p1 = clamp_point_to_window(p1, window);
p2 = clamp_point_to_window(p2, window);
/* Squaring has to happen before sorting so the area is squared from the point where
* dragging started. */
if (data->force_square) {
square_points(p1, p2);
square_points_clamp_to_window(p1, p2, window);
}
sort_points(p1, p2);
/* Drawing rect just out of the screenshot area to not capture the box in the picture. */
@@ -1254,7 +1281,7 @@ static inline void screenshot_area_transfer_to_rna(wmOperator *op, ScreenshotOpe
static wmOperatorStatus screenshot_preview_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
wmWindow *win = CTX_wm_window(C);
ScreenshotOperatorData *data = static_cast<ScreenshotOperatorData *>(op->customdata);
const int2 screen_space_cursor = {
@@ -1271,7 +1298,7 @@ static wmOperatorStatus screenshot_preview_modal(bContext *C, wmOperator *op, co
break;
case KM_RELEASE:
data->is_mouse_down = false;
data->drag_end = screen_space_cursor;
data->drag_end = clamp_point_to_window(screen_space_cursor, win);
screenshot_area_transfer_to_rna(op, data);
screenshot_preview_exec(C, op);
screenshot_preview_exit(C, op);
@@ -1327,27 +1354,39 @@ static wmOperatorStatus screenshot_preview_modal(bContext *C, wmOperator *op, co
}
case MOUSEMOVE: {
if (!data->crossed_threshold) {
const int2 delta = data->drag_end - data->drag_start;
if (std::abs(delta.x) > DRAG_THRESHOLD && std::abs(delta.y) > DRAG_THRESHOLD) {
/* Only set the points once the threshold has been crossed. This allows to just
* click to confirm using a potentially existing screenshot rect. */
data->crossed_threshold = true;
data->p1 = data->drag_start;
if (data->shift_area) {
const int2 delta = screen_space_cursor - data->last_cursor;
const int2 new_p1 = data->p1 + delta;
const int2 new_p2 = data->p2 + delta;
auto is_within_window = [win](const int2 &pt) -> bool {
return pt.x >= 0 && pt.x < win->sizex && pt.y >= 0 && pt.y < win->sizey;
};
/* Apply movement only if the entire rectangle stays within window bounds. */
if (is_within_window(new_p1) && is_within_window(new_p2)) {
data->p1 = new_p1;
data->p2 = new_p2;
}
}
else if (data->is_mouse_down) {
data->drag_end = clamp_point_to_window(screen_space_cursor, win);
if (!data->crossed_threshold) {
const int2 delta = data->drag_end - data->drag_start;
if (std::abs(delta.x) > DRAG_THRESHOLD && std::abs(delta.y) > DRAG_THRESHOLD) {
/* Only set the points once the threshold has been crossed. This allows to just
* click to confirm using a potentially existing screenshot rect. */
data->crossed_threshold = true;
data->p1 = data->drag_start;
}
}
if (data->crossed_threshold) {
data->p2 = data->drag_end;
}
}
if (data->shift_area) {
const int2 delta = screen_space_cursor - data->last_cursor;
data->p1 += delta;
data->p2 += delta;
}
else if (data->is_mouse_down) {
data->drag_end = screen_space_cursor;
if (data->crossed_threshold) {
data->p2 = screen_space_cursor;
}
}
CTX_wm_screen(C)->do_draw = true;
data->last_cursor = screen_space_cursor;
break;

View File

@@ -91,12 +91,10 @@ deform_tests = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
deform_tests.apply_modifiers = True
deform_tests.do_compare = True
deform_tests.run_all_tests()
break
elif cmd == "--run-test":
deform_tests.apply_modifiers = False
deform_tests.do_compare = False
name = command[i + 1]
deform_tests.run_test(name)

View File

@@ -342,12 +342,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
modifiers_test.apply_modifiers = True
modifiers_test.do_compare = True
modifiers_test.run_all_tests()
break
elif cmd == "--run-test":
modifiers_test.apply_modifiers = False
modifiers_test.do_compare = False
name = command[i + 1]
modifiers_test.run_test(name)

View File

@@ -814,7 +814,7 @@ class RunTest:
>>> modifiers_test.run_all_tests()
"""
def __init__(self, tests, apply_modifiers=False, do_compare=False):
def __init__(self, tests, do_compare=False):
"""
Construct a test suite.
@@ -825,10 +825,11 @@ class RunTest:
2) expected_object_name: bpy.Types.Object - expected object
3) modifiers or operators: list - list of mesh_test.ModifierSpec objects or
mesh_test.OperatorSpecEditMode objects
:arg do_compare: bool - Whether the result mesh will be compared with the provided golden mesh. When set to False
the modifier is not applied so the result can be examined inside Blender.
"""
self.tests = tests
self._ensure_unique_test_name_or_raise_error()
self.apply_modifiers = apply_modifiers
self.do_compare = do_compare
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
self._failed_tests_list = []
@@ -895,7 +896,9 @@ class RunTest:
raise Exception('No test called {} found!'.format(test_name))
test = case
test.apply_modifier = self.apply_modifiers
if not self.do_compare:
test.apply_modifier = False
test.do_compare = self.do_compare
success = test.run_test()

View File

@@ -35,12 +35,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
cloth_test.apply_modifiers = True
cloth_test.do_compare = True
cloth_test.run_all_tests()
break
elif cmd == "--run-test":
cloth_test.apply_modifiers = False
cloth_test.do_compare = False
name = command[i + 1]
cloth_test.run_test(name)

View File

@@ -26,12 +26,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
dynamic_paint_test.apply_modifiers = True
dynamic_paint_test.do_compare = True
dynamic_paint_test.run_all_tests()
break
elif cmd == "--run-test":
dynamic_paint_test.apply_modifiers = False
dynamic_paint_test.do_compare = False
name = command[i + 1]
dynamic_paint_test.run_test(name)

View File

@@ -22,12 +22,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
ocean_test.apply_modifiers = True
ocean_test.do_compare = True
ocean_test.run_all_tests()
break
elif cmd == "--run-test":
ocean_test.apply_modifiers = False
ocean_test.do_compare = False
name = command[i + 1]
ocean_test.run_test(name)

View File

@@ -24,12 +24,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
particle_instance_test.apply_modifiers = True
particle_instance_test.do_compare = True
particle_instance_test.run_all_tests()
break
elif cmd == "--run-test":
particle_instance_test.apply_modifiers = False
particle_instance_test.do_compare = False
name = command[i + 1]
particle_instance_test.run_test(name)

View File

@@ -27,12 +27,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
particle_test.apply_modifiers = True
particle_test.do_compare = True
particle_test.run_all_tests()
break
elif cmd == "--run-test":
particle_test.apply_modifiers = False
particle_test.do_compare = False
name = command[i + 1]
particle_test.run_test(name)

View File

@@ -24,12 +24,10 @@ def main():
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
soft_body_test.apply_modifiers = True
soft_body_test.do_compare = True
soft_body_test.run_all_tests()
break
elif cmd == "--run-test":
soft_body_test.apply_modifiers = False
soft_body_test.do_compare = False
name = command[i + 1]
soft_body_test.run_test(name)