feat: window snap angles

This commit is contained in:
galister 2026-07-14 18:04:36 +09:00
parent 779baaf86c
commit 30d786eaa1
14 changed files with 73 additions and 17 deletions

View File

@ -192,7 +192,9 @@
"APPLY_CHANGES_RESTART": "WayVR neu starten, um die Änderungen zu übernehmen?",
"COLOR_PALETTE_HELP": "Klicken, um Farbpaletten zu durchsuchen.\nUm eigene zu erstellen, besuche die Webseite!",
"COLOR_PALETTE_CUSTOM_URL": "Füge dein eigenes benutzerdefiniertes Thema hinzu!",
"COLOR_PALETTE_CUSTOM_URL2": "Klicken Sie hier, um das Web-Handbuch zu öffnen."
"COLOR_PALETTE_CUSTOM_URL2": "Klicken Sie hier, um das Web-Handbuch zu öffnen.",
"SNAP_ANGLE_DEG": "Snap-Winkel für Fensterneigung und Rollwinkel",
"SNAP_ANGLE_DEG_HELP": "Gegriffene Fenster rasten in diesem Winkel auf der Nick- und Rollachse ein."
},
"HELLO": "Hallo!",
"AUDIO": {

View File

@ -174,6 +174,8 @@
"SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens",
"SCROLL_SPEED": "Scroll speed",
"SELECT_VARIANT": "Select variant",
"SNAP_ANGLE_DEG": "Window pitch & roll snap angle",
"SNAP_ANGLE_DEG_HELP": "Grabbed windows snap to this angle on pitch and roll axis.",
"ENABLE_WATCH": "Enable watch",
"SETS_ON_WATCH": "Sets on watch",
"SHOW_WELCOME_SCREEN": "Show welcome screen",

View File

@ -192,7 +192,9 @@
"APPLY_CHANGES_RESTART": "¿Reiniciar WayVR para aplicar los cambios?",
"COLOR_PALETTE_HELP": "Haz clic para explorar paletas de colores.\nPara crear paletas personalizadas, ¡visita el sitio web!",
"COLOR_PALETTE_CUSTOM_URL": "¡Añade tu propio tema personalizado!",
"COLOR_PALETTE_CUSTOM_URL2": "Haz clic para abrir el manual web."
"COLOR_PALETTE_CUSTOM_URL2": "Haz clic para abrir el manual web.",
"SNAP_ANGLE_DEG": "Ángulo de ajuste de cabeceo y alabeo de la ventana",
"SNAP_ANGLE_DEG_HELP": "Las ventanas agarradas se ajustan a este ángulo en los ejes de cabeceo y balanceo."
},
"HELLO": "¡Hola!",
"AUDIO": {

View File

@ -213,7 +213,9 @@
"APPLY_CHANGES_RESTART": "Riavviare WayVR per applicare le modifiche?",
"COLOR_PALETTE_HELP": "Clicca per sfogliare le palette di colori.\nPer crearne di personalizzate, visita il sito web!",
"COLOR_PALETTE_CUSTOM_URL": "Aggiungi il tuo tema personalizzato!",
"COLOR_PALETTE_CUSTOM_URL2": "Clicca per aprire il manuale web."
"COLOR_PALETTE_CUSTOM_URL2": "Clicca per aprire il manuale web.",
"SNAP_ANGLE_DEG": "Angolo di scatto beccheggio e rollio finestra",
"SNAP_ANGLE_DEG_HELP": "Le finestre afferrate si allineano a questo angolo sugli assi di beccheggio e rollio."
},
"APPLICATION_LAUNCHER": "Lanciatore applicazioni",
"APPLICATION_STARTED": "Applicazione avviata",

View File

@ -192,7 +192,9 @@
"APPLY_CHANGES_RESTART": "変更を適用するためにWayVRを再起動しますか",
"COLOR_PALETTE_HELP": "クリックしてカラーパレットを選択してください。\nカスタムパレットの作成については、ウェブサイトを参照してください",
"COLOR_PALETTE_CUSTOM_URL": "カスタムテーマを追加しましょう!",
"COLOR_PALETTE_CUSTOM_URL2": "クリックしてウェブマニュアルを開く。"
"COLOR_PALETTE_CUSTOM_URL2": "クリックしてウェブマニュアルを開く。",
"SNAP_ANGLE_DEG": "ウィンドウのピッチ・ロールのスナップ角度",
"SNAP_ANGLE_DEG_HELP": "掴んでいるウィンドウが、ピッチ軸とロール軸でこの角度にスナップします。"
},
"HELLO": "こんにちは!",
"AUDIO": {

View File

@ -187,7 +187,9 @@
"APPLY_CHANGES_RESTART": "Zrestartować WayVR, aby zastosować zmiany?",
"COLOR_PALETTE_HELP": "Kliknij, aby przeglądać palety kolorów.\nAby stworzyć własne, odwiedź stronę internetową!",
"COLOR_PALETTE_CUSTOM_URL": "Dodaj własny motyw!",
"COLOR_PALETTE_CUSTOM_URL2": "Kliknij, aby otworzyć instrukcję internetową."
"COLOR_PALETTE_CUSTOM_URL2": "Kliknij, aby otworzyć instrukcję internetową.",
"SNAP_ANGLE_DEG": "Kąt przyciągania pochylenia i przechyłu okna",
"SNAP_ANGLE_DEG_HELP": "Chwycone okna przyciągają się do tego kąta na osi pochylenia i obrotu."
},
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APPLICATIONS": "Aplikacje",

View File

@ -213,7 +213,9 @@
"APPLY_CHANGES_RESTART": "重启 WayVR 以应用更改吗?",
"COLOR_PALETTE_HELP": "点击浏览配色方案。\n如需创建自定义配色请参阅网站",
"COLOR_PALETTE_CUSTOM_URL": "添加自定义主题!",
"COLOR_PALETTE_CUSTOM_URL2": "点击打开网页版手册。"
"COLOR_PALETTE_CUSTOM_URL2": "点击打开网页版手册。",
"SNAP_ANGLE_DEG": "窗口俯仰与翻滚捕捉角度",
"SNAP_ANGLE_DEG_HELP": "抓取的窗口在俯仰轴和翻滚轴上将对齐到此角度。"
},
"APPLICATION_LAUNCHER": "应用启动器",
"APPLICATION_STARTED": "应用已启动",

View File

@ -320,6 +320,7 @@ pub(crate) enum SettingType {
PointerLerpFactor,
ScreenRenderDown,
ScrollSpeed,
SnapAngleDeg,
EnableWatch,
SetsOnWatch,
SpaceDragMultiplier,
@ -389,6 +390,7 @@ impl SettingType {
Self::LongPressDuration => &mut config.long_press_duration,
Self::PointerLerpFactor => &mut config.pointer_lerp_factor,
Self::ScrollSpeed => &mut config.scroll_speed,
Self::SnapAngleDeg => &mut config.snap_angle_deg,
Self::SpaceDragMultiplier => &mut config.space_drag_multiplier,
Self::SpaceGravityDamping => &mut config.space_gravity_damping,
Self::SpaceGravityFlingStrength => &mut config.space_gravity_fling_strength,
@ -500,6 +502,7 @@ impl SettingType {
Self::ScrollSpeed => Ok("APP_SETTINGS.SCROLL_SPEED"),
Self::EnableWatch => Ok("APP_SETTINGS.ENABLE_WATCH"),
Self::SetsOnWatch => Ok("APP_SETTINGS.SETS_ON_WATCH"),
Self::SnapAngleDeg => Ok("APP_SETTINGS.SNAP_ANGLE_DEG"),
Self::SpaceDragMultiplier => Ok("APP_SETTINGS.SPACE_DRAG_MULTIPLIER"),
Self::SpaceDragUnlocked => Ok("APP_SETTINGS.SPACE_DRAG_UNLOCKED"),
Self::SpaceGravityDamping => Ok("APP_SETTINGS.SPACE_GRAVITY_DAMPING"),
@ -536,6 +539,7 @@ impl SettingType {
Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"),
Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"),
Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"),
Self::SnapAngleDeg => Some("APP_SETTINGS.SNAP_ANGLE_DEG_HELP"),
Self::SpaceGravityDamping => Some("APP_SETTINGS.SPACE_GRAVITY_DAMPING_HELP"),
Self::SpaceGravityFlingStrength => Some("APP_SETTINGS.SPACE_GRAVITY_FLING_STRENGTH_HELP"),
Self::SpaceGravityFloorHeight => Some("APP_SETTINGS.SPACE_GRAVITY_FLOOR_HEIGHT_HELP"),

View File

@ -10,10 +10,10 @@ use crate::util::popup_manager::PopupHolder;
use crate::{
frontend::FrontendTasks,
tab::settings::{
SettingType, SettingsMountParams, SettingsTab,
macros::{options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32},
SettingType, SettingsMountParams, SettingsTab,
},
views::{ViewUpdateParams, input_profiles},
views::{input_profiles, ViewUpdateParams},
};
#[derive(Clone)]
@ -104,6 +104,7 @@ impl State {
options_checkbox(par.mp, c, SettingType::InvertScrollDirectionY)?;
options_slider_f32(par.mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?;
options_slider_f32(par.mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?;
options_slider_f32(par.mp, c, SettingType::SnapAngleDeg, 0., 45., 5.)?;
if par.feats.openxr {
options_slider_f32(par.mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?;

View File

@ -888,7 +888,12 @@ where
app.anchor.translation =
pointer.pose.transform_point3a(grab_data.offset.translation);
let scale = scalar_scale(&app.anchor);
realign(&mut app.anchor, &app.input_state.hmd, scale);
realign(
&mut app.anchor,
&app.input_state.hmd,
scale,
app.session.config.snap_angle_deg,
);
}
} else {
// single grab resize
@ -912,7 +917,12 @@ where
let scale = scalar_scale(&overlay_state.transform);
overlay_state.transform.translation =
pointer.pose.transform_point3a(grab_data.offset.translation);
realign(&mut overlay_state.transform, &app.input_state.hmd, scale);
realign(
&mut overlay_state.transform,
&app.input_state.hmd,
scale,
app.session.config.snap_angle_deg,
);
}
overlay.config.pause_movement = true;
overlay.config.dirty = true;

View File

@ -187,6 +187,7 @@ pub struct AutoSettings {
pub whisper_model: Arc<str>,
pub default_overlay_scale: f32,
pub color_palette: Arc<str>,
pub snap_angle_deg: f32,
}
fn get_settings_path() -> PathBuf {
@ -252,6 +253,7 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
whisper_model: config.whisper_model.clone(),
default_overlay_scale: config.default_overlay_scale,
color_palette: config.color_palette.clone(),
snap_angle_deg: config.snap_angle_deg,
};
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic

View File

@ -329,7 +329,7 @@ fn tutorial_spawn_effect(app: &mut AppState) -> anyhow::Result<()> {
let hmd = &app.input_state.hmd;
let pos = hmd.translation + hmd.z_axis * -2.0;
let mut transform = Affine3A::from_rotation_translation(Quat::IDENTITY, pos.into());
realign(&mut transform, hmd, 1.0);
realign(&mut transform, hmd, 1.0, app.session.config.snap_angle_deg);
owc.active_state = Some(OverlayWindowState {
saved_transform: Some(Affine3A::from_translation(vec3(0., -0.1, -0.9))),

View File

@ -1,4 +1,4 @@
use glam::{Affine3A, Mat3A, Quat, Vec3, Vec3A};
use glam::{Affine3A, EulerRot, Mat3A, Quat, Vec3, Vec3A};
use idmap_derive::IntegerId;
use std::{f32::consts::PI, sync::Arc};
use wlx_common::windowing::{OverlayWindowState, Positioning};
@ -192,7 +192,12 @@ impl OverlayWindowConfig {
};
if align_to_hmd {
realign(&mut state.transform, &app.input_state.hmd, scale);
realign(
&mut state.transform,
&app.input_state.hmd,
scale,
app.session.config.snap_angle_deg,
);
}
self.dirty = true;
@ -250,13 +255,18 @@ impl OverlayWindowConfig {
if align_to_hmd || (state.grabbable && hard_reset) {
let scale = scalar_scale(&cur_transform);
realign(&mut state.transform, &app.input_state.hmd, scale);
realign(
&mut state.transform,
&app.input_state.hmd,
scale,
app.session.config.snap_angle_deg,
);
}
self.dirty = true;
}
}
pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32) {
pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32, snap_angle: f32) {
let to_hmd = hmd.translation - transform.translation;
let up_dir: Vec3A;
@ -289,8 +299,20 @@ pub fn realign(transform: &mut Affine3A, hmd: &Affine3A, scale: f32) {
let col_y = col_z.cross(col_x).normalize();
let col_x = col_x.normalize();
let rot = Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI));
transform.matrix3 = Mat3A::from_cols(col_x, col_y, col_z).mul_scalar(scale) * rot;
let flip = Mat3A::from_quat(Quat::from_axis_angle(Vec3::Y, PI));
let mut rotation = Mat3A::from_cols(col_x, col_y, col_z) * flip;
if snap_angle > f32::EPSILON {
let snap_step = snap_angle.to_radians();
let (yaw, pitch, roll) = rotation.to_euler(EulerRot::YXZ);
let pitch = (pitch / snap_step).round() * snap_step;
let roll = (roll / snap_step).round() * snap_step;
rotation = Mat3A::from_euler(EulerRot::YXZ, yaw, pitch, roll);
}
transform.matrix3 = rotation.mul_scalar(scale);
}
pub fn scalar_scale(a: &Affine3A) -> f32 {

View File

@ -475,4 +475,7 @@ pub struct GeneralConfig {
#[serde(default = "def_default")]
pub color_palette: Arc<str>,
#[serde(default)]
pub snap_angle_deg: f32,
}