From 9425937bf6cb38e0bf0c1d46396fcad46aa9d6c3 Mon Sep 17 00:00:00 2001 From: Michael Kirsch Date: Mon, 20 Dec 2021 13:30:29 +0100 Subject: [PATCH] UI: Preserve position during alignment change --- UI/window-basic-transform.cpp | 27 ++++++++++++++- UI/window-basic-transform.hpp | 2 ++ docs/sphinx/reference-scenes.rst | 7 ++++ libobs/obs-scene.c | 57 ++++++++++++++++++++++++++++---- libobs/obs.h | 5 ++- 5 files changed, 90 insertions(+), 8 deletions(-) diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp index c9f7da75047e57..83681809e5cafd 100644 --- a/UI/window-basic-transform.cpp +++ b/UI/window-basic-transform.cpp @@ -52,7 +52,7 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent) HookWidget(ui->rotation, DSCROLL_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->sizeX, DSCROLL_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->sizeY, DSCROLL_CHANGED, SLOT(OnControlChanged())); - HookWidget(ui->align, COMBO_CHANGED, SLOT(OnControlChanged())); + HookWidget(ui->align, COMBO_CHANGED, SLOT(OnAlignmentChanged())); HookWidget(ui->boundsType, COMBO_CHANGED, SLOT(OnBoundsType(int))); HookWidget(ui->boundsAlign, COMBO_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->boundsWidth, DSCROLL_CHANGED, SLOT(OnControlChanged())); @@ -251,6 +251,7 @@ void OBSBasicTransform::RefreshControls() int boundsAlignIndex = AlignToList(osi.bounds_alignment); ignoreItemChange = true; + ui->positionX->setValue(osi.pos.x); ui->positionY->setValue(osi.pos.y); ui->rotation->setValue(osi.rot); @@ -267,6 +268,9 @@ void OBSBasicTransform::RefreshControls() ui->cropRight->setValue(int(crop.right)); ui->cropTop->setValue(int(crop.top)); ui->cropBottom->setValue(int(crop.bottom)); + + oti_save = osi; + ignoreItemChange = false; std::string name = obs_source_get_name(source); @@ -324,9 +328,30 @@ void OBSBasicTransform::OnControlChanged() ignoreTransformSignal = true; obs_sceneitem_set_info(item, &oti); + oti_save = oti; ignoreTransformSignal = false; } +void OBSBasicTransform::OnAlignmentChanged() +{ + if (ignoreItemChange) + return; + + uint32_t newAlignment = listToAlign[ui->align->currentIndex()]; + struct vec2 offset; + + obs_sceneitem_alignment_get_vecdiff(item, &offset, oti_save.alignment, + newAlignment); + + ignoreItemChange = true; + + ui->positionX->setValue(oti_save.pos.x + offset.x); + ui->positionY->setValue(oti_save.pos.y + offset.y); + + ignoreItemChange = false; + OnControlChanged(); +} + void OBSBasicTransform::OnCropChanged() { if (ignoreItemChange) diff --git a/UI/window-basic-transform.hpp b/UI/window-basic-transform.hpp index fcbf4b35af013f..fb942e7509c0a0 100644 --- a/UI/window-basic-transform.hpp +++ b/UI/window-basic-transform.hpp @@ -23,6 +23,7 @@ class OBSBasicTransform : public QDialog { OBSSignal deselectSignal; std::string undo_data; + obs_transform_info oti_save; bool ignoreTransformSignal = false; bool ignoreItemChange = false; @@ -44,6 +45,7 @@ private slots: void SetItemQt(OBSSceneItem newItem); void OnBoundsType(int index); void OnControlChanged(); + void OnAlignmentChanged(); void OnCropChanged(); void on_resetButton_clicked(); diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst index 86eae5c8ec0e92..8c36ef8dbb7c50 100644 --- a/docs/sphinx/reference-scenes.rst +++ b/docs/sphinx/reference-scenes.rst @@ -458,6 +458,13 @@ Scene Item Functions --------------------- +.. function:: void obs_sceneitem_alignment_get_vecdiff(const obs_sceneitem_t *item, struct vec2 *offset, uint32_t align_from, uint32_t align_to) + + Gets the positional difference when switching between different alignment + settings for the scene item. + +--------------------- + .. function:: void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform) Gets the transform matrix of the scene item used for drawing the diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index c09c7a1cd3efbd..75301f69df972f 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -21,6 +21,7 @@ #include "graphics/math-defs.h" #include "obs-scene.h" #include "obs-internal.h" +#include const struct obs_source_info group_info; @@ -358,7 +359,7 @@ void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy) v->y += (float)(cy / 2); } -static void calculate_bounds_data(struct obs_scene_item *item, +static void calculate_bounds_data(const struct obs_scene_item *item, struct vec2 *origin, struct vec2 *scale, uint32_t *cx, uint32_t *cy) { @@ -431,8 +432,6 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) struct vec2 base_origin; struct vec2 origin; struct vec2 scale; - struct calldata params; - uint8_t stack[128]; if (os_atomic_load_long(&item->defer_update) > 0) return; @@ -499,9 +498,13 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) /* ----------------------- */ - calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "item", item); - signal_parent(item->parent, "item_transform", ¶ms); + if (item->parent) { + struct calldata params; + uint8_t stack[128]; + calldata_init_fixed(¶ms, stack, sizeof(stack)); + calldata_set_ptr(¶ms, "item", item); + signal_parent(item->parent, "item_transform", ¶ms); + } if (!update_tex) return; @@ -2733,6 +2736,48 @@ void obs_sceneitem_set_info(obs_sceneitem_t *item, } } +void obs_sceneitem_alignment_get_vecdiff(const obs_sceneitem_t *item, + struct vec2 *offset, + uint32_t align_from, uint32_t align_to) +{ + if (!item || !offset) + return; + + // Mathematical background based on update_item_transform: + // matrix4 children seen as rows (implied in matrix multiply function): + // T_i = Eye(4) * M_Scale * M_Align_i^(-1) * M_Rot * M_Pos_i + // Original alignment transformation: + // T_1 (stored in mat_T1) + // = Eye(4) * M_Scale * M_Align_1^(-1) * M_Rot * M_Pos_1 + // New alignment transformation without position adaptation: + // T_2* (stored in mat_T2_star) + // = Eye(4) * M_Scale * M_Align_2^(-1) * M_Rot * M_Pos_1 + // New whole transformation should be equal to previous + // T_2 = T_2* * M_pos_12 =? T_1 + // -> + // M_pos_12 = (T_2*)^(-1) * T_1 + + struct matrix4 mat_T1, mat_T2_star, mat_t12; + + obs_sceneitem_t temp_item = *item; + + // prevent signals to be sent to the scene + temp_item.parent = NULL; + + temp_item.align = align_from; + update_item_transform(&temp_item, false); + obs_sceneitem_get_draw_transform(&temp_item, &mat_T1); + + temp_item.align = align_to; + update_item_transform(&temp_item, false); + obs_sceneitem_get_draw_transform(&temp_item, &mat_T2_star); + matrix4_inv(&mat_T2_star, &mat_T2_star); + + matrix4_mul(&mat_t12, &mat_T2_star, &mat_T1); + + vec2_set(offset, mat_t12.t.x, mat_t12.t.y); +} + void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform) { diff --git a/libobs/obs.h b/libobs/obs.h index 82a043d2cdf6b0..28aea0cbae5613 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -1819,7 +1819,10 @@ EXPORT void obs_sceneitem_get_info(const obs_sceneitem_t *item, struct obs_transform_info *info); EXPORT void obs_sceneitem_set_info(obs_sceneitem_t *item, const struct obs_transform_info *info); - +EXPORT void obs_sceneitem_alignment_get_vecdiff(const obs_sceneitem_t *item, + struct vec2 *offset, + uint32_t align_from, + uint32_t align_to); EXPORT void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform); EXPORT void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item,