Skip to content

Commit 1074a03

Browse files
Add auto lock on reverse
1 parent 476bec6 commit 1074a03

File tree

2 files changed

+99
-11
lines changed

2 files changed

+99
-11
lines changed

obs-zoom-to-mouse.lua

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ local zoom_info = {
3131
local zoom_time = 0
3232
local zoom_target = nil
3333
local locked_center = nil
34+
local locked_last_pos = nil
3435
local hotkey_zoom_id = nil
3536
local hotkey_follow_id = nil
3637
local is_timer_running = false
@@ -42,6 +43,7 @@ local is_following_mouse = false
4243
local follow_speed = 0.1
4344
local follow_border = 0
4445
local follow_safezone_sensitivity = 10
46+
local use_follow_auto_lock = false
4547
local zoom_value = 2
4648
local zoom_speed = 0.1
4749
local use_monitor_override = false
@@ -575,6 +577,7 @@ function on_toggle_zoom(pressed)
575577
zoom_state = ZoomState.ZoomingOut
576578
zoom_time = 0
577579
locked_center = nil
580+
locked_last_pos = nil
578581
zoom_target = { crop = crop_filter_info_orig, c = sceneitem_crop_orig }
579582
if is_following_mouse then
580583
is_following_mouse = false
@@ -587,6 +590,7 @@ function on_toggle_zoom(pressed)
587590
zoom_info.zoom_to = zoom_value
588591
zoom_time = 0
589592
locked_center = nil
593+
locked_last_pos = nil
590594
zoom_target = get_target_position(zoom_info)
591595
end
592596

@@ -608,6 +612,11 @@ function on_timer()
608612
if zoom_state == ZoomState.ZoomingOut or zoom_state == ZoomState.ZoomingIn then
609613
-- When we are doing a zoom animation (in or out) we linear interpolate the crop to the target
610614
if zoom_time <= 1 then
615+
-- If we have auto-follow turned on, make sure to keep the mouse in the view while we zoom
616+
-- This is incase the user is moving the mouse a lot while the animation (which may be slow) is playing
617+
if zoom_state == ZoomState.ZoomingIn and use_auto_follow_mouse then
618+
zoom_target = get_target_position(zoom_info)
619+
end
611620
crop_filter_info.x = lerp(crop_filter_info.x, zoom_target.crop.x, zoom_time)
612621
crop_filter_info.y = lerp(crop_filter_info.y, zoom_target.crop.y, zoom_time)
613622
crop_filter_info.w = lerp(crop_filter_info.w, zoom_target.crop.w, zoom_time)
@@ -647,6 +656,12 @@ function on_timer()
647656
if math.abs(diff.x) > track.x or math.abs(diff.y) > track.y then
648657
-- Cursor moved into the active border area, so resume tracking by clearing out the locked_center
649658
locked_center = nil
659+
locked_last_pos = {
660+
x = zoom_target.raw_center.x,
661+
y = zoom_target.raw_center.y,
662+
diff_x = diff.x,
663+
diff_y = diff.y
664+
}
650665
log("Locked area exited - resume tracking")
651666
end
652667
end
@@ -657,15 +672,35 @@ function on_timer()
657672
set_crop_settings(crop_filter_info)
658673

659674
-- Check to see if the mouse has stopped moving long enough to create a new safe zone
660-
if is_following_mouse and locked_center == nil then
675+
if is_following_mouse and locked_center == nil and locked_last_pos ~= nil then
661676
local diff = {
662677
x = math.abs(crop_filter_info.x - zoom_target.crop.x),
663-
y = math.abs(crop_filter_info.y - zoom_target.crop.y)
678+
y = math.abs(crop_filter_info.y - zoom_target.crop.y),
679+
auto_x = zoom_target.raw_center.x - locked_last_pos.x,
680+
auto_y = zoom_target.raw_center.y - locked_last_pos.y
664681
}
665682

666-
if diff.x <= follow_safezone_sensitivity and diff.y <= follow_safezone_sensitivity then
667-
locked_center = { x = zoom_target.clamped_center.x, y = zoom_target.clamped_center.y }
668-
log("Cursor stopped. Tracking locked to " .. locked_center.x .. ", " .. locked_center.y )
683+
locked_last_pos.x = zoom_target.raw_center.x
684+
locked_last_pos.y = zoom_target.raw_center.y
685+
686+
local lock = false
687+
if math.abs(locked_last_pos.diff_x) > math.abs(locked_last_pos.diff_y) then
688+
if (diff.auto_x < 0 and locked_last_pos.diff_x > 0) or (diff.auto_x > 0 and locked_last_pos.diff_x < 0) then
689+
lock = true
690+
end
691+
else
692+
if (diff.auto_y < 0 and locked_last_pos.diff_y > 0) or (diff.auto_y > 0 and locked_last_pos.diff_y < 0) then
693+
lock = true
694+
end
695+
end
696+
697+
if (lock and use_follow_auto_lock) or (diff.x <= follow_safezone_sensitivity and diff.y <= follow_safezone_sensitivity) then
698+
-- Make the new center the position of the current camera (which might not be the same as the mouse since we lerp towards it)
699+
locked_center = {
700+
x = math.floor(crop_filter_info.x + zoom_target.crop.w * 0.5),
701+
y = math.floor(crop_filter_info.y + zoom_target.crop.h * 0.5)
702+
}
703+
log("Cursor stopped. Tracking locked to " .. locked_center.x .. ", " .. locked_center.y)
669704
end
670705
end
671706
end
@@ -735,15 +770,48 @@ function on_update_transform()
735770
end
736771

737772
function on_settings_modified(props, prop, settings)
773+
local name = obs.obs_property_name(prop)
774+
738775
-- Show/Hide the settings based on if the checkbox is checked or not
739-
local visible = obs.obs_data_get_bool(settings, "use_monitor_override")
740-
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_x"), visible)
741-
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_y"), visible)
742-
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_w"), visible)
743-
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_h"), visible)
776+
if name == "use_monitor_override" then
777+
local visible = obs.obs_data_get_bool(settings, "use_monitor_override")
778+
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_x"), visible)
779+
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_y"), visible)
780+
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_w"), visible)
781+
obs.obs_property_set_visible(obs.obs_properties_get(props, "monitor_override_h"), visible)
782+
elseif name == "debug_logs" then
783+
if obs.obs_data_get_bool(settings, "debug_logs") then
784+
log_current_settings()
785+
end
786+
end
787+
744788
return true
745789
end
746790

791+
---
792+
-- Write the current settings into the log for debugging and user issue reports
793+
function log_current_settings()
794+
local settings = {
795+
zoom_value = zoom_value,
796+
zoom_speed = zoom_speed,
797+
use_auto_follow_mouse = use_auto_follow_mouse,
798+
use_follow_outside_bounds = use_follow_outside_bounds,
799+
follow_speed = follow_speed,
800+
follow_border = follow_border,
801+
follow_safezone_sensitivity = follow_safezone_sensitivity,
802+
use_follow_auto_lock = use_follow_auto_lock,
803+
use_monitor_override = use_monitor_override,
804+
monitor_override_x = monitor_override_x,
805+
monitor_override_y = monitor_override_y,
806+
monitor_override_w = monitor_override_w,
807+
monitor_override_h = monitor_override_h,
808+
debug_logs = debug_logs
809+
}
810+
811+
log("Current settings:")
812+
log(format_table(settings))
813+
end
814+
747815
function on_print_help()
748816
local help = "\n----------------------------------------------------\n" ..
749817
"Help Information for OBS-Zoom-To-Mouse v" .. VERSION .. "\n" ..
@@ -759,6 +827,7 @@ function on_print_help()
759827
"Follow Speed: The speed at which the zoomed area will follow the mouse when tracking\n" ..
760828
"Follow Border: The %distance from the edge of the source that will re-enable mouse tracking\n" ..
761829
"Lock Sensitivity: How close the tracking needs to get before it locks into position and stops tracking until you enter the follow border\n" ..
830+
"Auto Lock on reverse direction: Automatically stop tracking if you reverse the direction of the mouse\n" ..
762831
"Set manual monitor position: True to override the calculated x,y topleft position for the selected display\n" ..
763832
"X: The coordinate of the left most pixel of the display\n" ..
764833
"Y: The coordinate of the top most pixel of the display\n" ..
@@ -809,6 +878,7 @@ function script_properties()
809878
local follow_border = obs.obs_properties_add_int_slider(props, "follow_border", "Follow Border", 0, 50, 1)
810879
local safezone_sense = obs.obs_properties_add_int_slider(props,
811880
"follow_safezone_sensitivity", "Lock Sensitivity", 1, 20, 1)
881+
local follow_auto_lock = obs.obs_properties_add_bool(props, "follow_auto_lock", "Auto Lock on reverse direction")
812882

813883
local override = obs.obs_properties_add_bool(props, "use_monitor_override", "Set manual monitor position")
814884
local override_x = obs.obs_properties_add_int(props, "monitor_override_x", "X", 0, 10000, 1)
@@ -825,6 +895,7 @@ function script_properties()
825895
obs.obs_property_set_visible(override_w, use_monitor_override)
826896
obs.obs_property_set_visible(override_h, use_monitor_override)
827897
obs.obs_property_set_modified_callback(override, on_settings_modified)
898+
obs.obs_property_set_modified_callback(debug, on_settings_modified)
828899

829900
return props
830901
end
@@ -856,6 +927,7 @@ function script_load(settings)
856927
follow_speed = obs.obs_data_get_double(settings, "follow_speed")
857928
follow_border = obs.obs_data_get_int(settings, "follow_border")
858929
follow_safezone_sensitivity = obs.obs_data_get_int(settings, "follow_safezone_sensitivity")
930+
use_follow_auto_lock = obs.obs_data_get_bool(settings, "follow_auto_lock")
859931
use_monitor_override = obs.obs_data_get_bool(settings, "use_monitor_override")
860932
monitor_override_x = obs.obs_data_get_int(settings, "monitor_override_x")
861933
monitor_override_y = obs.obs_data_get_int(settings, "monitor_override_y")
@@ -864,6 +936,10 @@ function script_load(settings)
864936
debug_logs = obs.obs_data_get_bool(settings, "debug_logs")
865937

866938
obs.obs_frontend_add_event_callback(on_frontend_event)
939+
940+
if debug_logs then
941+
log_current_settings()
942+
end
867943
end
868944

869945
function script_unload()
@@ -883,6 +959,7 @@ function script_defaults(settings)
883959
obs.obs_data_set_default_double(settings, "follow_speed", 0.1)
884960
obs.obs_data_set_default_int(settings, "follow_border", 2)
885961
obs.obs_data_set_default_int(settings, "follow_safezone_sensitivity", 4)
962+
obs.obs_data_set_default_bool(settings, "follow_auto_lock", false)
886963
obs.obs_data_set_default_bool(settings, "use_monitor_override", false)
887964
obs.obs_data_set_default_int(settings, "monitor_override_x", 0)
888965
obs.obs_data_set_default_int(settings, "monitor_override_y", 0)
@@ -918,6 +995,7 @@ function script_update(settings)
918995
follow_speed = obs.obs_data_get_double(settings, "follow_speed")
919996
follow_border = obs.obs_data_get_int(settings, "follow_border")
920997
follow_safezone_sensitivity = obs.obs_data_get_int(settings, "follow_safezone_sensitivity")
998+
use_follow_auto_lock = obs.obs_data_get_bool(settings, "follow_auto_lock")
921999
use_monitor_override = obs.obs_data_get_bool(settings, "use_monitor_override")
9221000
monitor_override_x = obs.obs_data_get_int(settings, "monitor_override_x")
9231001
monitor_override_y = obs.obs_data_get_int(settings, "monitor_override_y")

readme.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ Inspired by [tryptech](https://github.com/tryptech)'s [obs-zoom-and-follow](http
4040
* **Force transform update**: Click to refresh the internal zoom data if you manually change the transform/filters on your zoom source
4141
* **Zoom Factor**: How much to zoom in by
4242
* **Zoom Speed**: The speed of the zoom in/out animation
43-
* **Auto follow mouse**: True to track the cursor while you are zoomed in instead of waiting for the toggle follow hotkey
43+
* **Auto follow mouse**: True to track the cursor automatically while you are zoomed in, instead of waiting for the `Toggle follow` hotkey to be pressed first
4444
* **Follow outside bounds**: True to track the cursor even when it is outside the bounds of the source
4545
* **Follow Speed**: The speed at which the zoomed area will follow the mouse when tracking
4646
* **Follow Border**: The %distance from the edge of the source that will re-enable mouse tracking
4747
* **Lock Sensitivity**: How close the tracking needs to get before it locks into position and stops tracking until you enter the follow border
48+
* **Auto Lock on reverse direction**: Automatically stop tracking if you reverse the direction of the mouse.
4849
* **Set manual monitor position**: True to override the calculated x,y topleft position for the selected display
4950
* **X**: The coordinate of the left most pixel of the display
5051
* **Y**: The coordinate of the top most pixel of the display
@@ -57,6 +58,15 @@ Inspired by [tryptech](https://github.com/tryptech)'s [obs-zoom-and-follow](http
5758
* Add a hotkey for `Toggle zoom to mouse` to zoom in and out
5859
* Add a hotkey for `Toggle follow mouse during zoom` to turn mouse tracking on and off (*Optional*)
5960

61+
### More information on how mouse tracking works
62+
When you press the `Toggle zoom` hotkey the script will use the current mouse position as the center of the zoom. The script will then animate the width/height values of a crop/pan filter so it appears to zoom into that location. If you have `Auto follow mouse` turned on, then the x/y values of the filter will also change to keep the mouse in view as it is animating the zoom. Once the animation is complete, the script gives you a "safe zone" to move your cursor in without it moving the "camera". The idea was that you'd want to zoom in somewhere and move your mouse around to highlight code or whatever, without the screen moving so it would be easier to read text in the video.
63+
64+
When you move your mouse to the edge of the zoom area, it will then start tracking the cursor and follow it around at the `Follow Speed`. It will continue to follow the cursor until you hold the mouse still for some amount of time determined by `Lock Sensitivity` at which point it will stop following and give you that safe zone again but now at the new center of the zoom.
65+
66+
How close you need to get to the edge of the zoom to trigger the 'start following mode' is determined by the `Follow Border` setting. This value is a pertentage of the area from the edge. If you set this to 0%, it means that you need to move the mouse to the very edge of the area to trigger mouse tracking. Something like 4% will give you a small border around the area. Setting it to full 50% causes it to begin following the mouse whenever it gets closer than 50% to an edge, which means it will follow the cursor *all the time* essentially removing the "safe zone".
67+
68+
You can also modify this behavior with the `Auto Lock on reverse direction` setting, which attempts to make the follow work more like camera panning in a video game. When moving your mouse to the edge of the screen (how close determined by `Follow Border`) it will cause the camera to pan in that direction. Instead of continuing to track the mouse until you keep it still, with this setting it will also stop tracking immediately if you move your mouse back towards the center.
69+
6070
## Known Limitations
6171
* Currently this script only works on **Windows**
6272
* Internally it uses [FFI](https://luajit.org/ext_ffi.html) to get the mouse position by loading the Win32 `GetCursorPos()` function

0 commit comments

Comments
 (0)