mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 00:00:05 +02:00
Add session-only anchored overlay resize grip
This commit is contained in:
1 parent
468d3e9b63
commit
19a3db7667
9 files changed
+94
-11
No files matched your search
@@ -29,6 +29,8 @@ See [third-party notes](docs/third-party.md). No GitHub release is published yet
|
||||
Nothing inserts or submits automatically after transcription.
|
||||
- **Overlay:** Record/Stop, Cancel, paginated preview, Insert, Enter and Quit.
|
||||
Review and settings share one Inconsolata/neon-framed surface.
|
||||
Drag the inset lower-right grip to resize the panel (world, head or wrist);
|
||||
the upper-left corner stays anchored. Size changes last for this run only.
|
||||
**Bindings** requests SteamVR's binding editor directly. Existing SteamVR overrides may supersede defaults. World-space by
|
||||
default; settings offer left wrist, right wrist and head mounting, plus recenter.
|
||||
Dashboard lasers provide clickable controls. Settings → Lasers anytime is an
|
||||
|
||||
+7
-1
@@ -181,7 +181,13 @@ rather than placing a menu at the world origin. A tracking-origin reset requests
|
||||
a fresh placement. Settings → Recenter in front deliberately resamples the pose.
|
||||
|
||||
Settings offers World space, Left wrist, Right wrist and Head on that same canvas.
|
||||
World/head width is 0.85 m; wrist width defaults to 0.30 m. The left wrist uses
|
||||
World/head width is 0.85 m; wrist width defaults to 0.30 m. All mounts have
|
||||
the same lower-right resize grip: drag it to scale the panel
|
||||
between half and twice its configured width. The original upper-left corner
|
||||
remains fixed relative to the chosen mount, rather than scaling about the
|
||||
center. The physical resize is session-only; switching mounts retains the
|
||||
scale factor, while restart restores configured defaults. Grip behavior and
|
||||
comfort still need an opt-in on-headset check. The left wrist uses
|
||||
VR Workspace's fallback watch-face axes: panel-right points toward the fingers
|
||||
(controller -Z), panel-up points out of the back of the hand (controller +Y),
|
||||
and panel-front points toward controller +X. The right wrist reverses panel-right
|
||||
|
||||
@@ -95,6 +95,14 @@ float mount_width(Mount mount, const WristPlacement& wrist) {
|
||||
return mount == Mount::LeftWrist || mount == Mount::RightWrist ? wrist.width : 0.85f;
|
||||
}
|
||||
|
||||
Matrix34 resized_mount_pose(Matrix34 pose, float original_width, float scale, float aspect) {
|
||||
const float dx = original_width * (scale - 1.f) * .5f;
|
||||
const float dy = -dx * aspect;
|
||||
for (int row = 0; row < 3; ++row)
|
||||
pose[row][3] += pose[row][0] * dx + pose[row][1] * dy;
|
||||
return pose;
|
||||
}
|
||||
|
||||
std::filesystem::path default_mount_settings_path() {
|
||||
try {
|
||||
const char* xdg = std::getenv("XDG_CONFIG_HOME");
|
||||
|
||||
@@ -29,6 +29,9 @@ std::optional<Matrix34> world_mount_pose(const Matrix34& hmd);
|
||||
// Device-relative transform; World has no relative anchor and returns identity.
|
||||
Matrix34 relative_mount_pose(Mount mount, const WristPlacement& wrist = {});
|
||||
float mount_width(Mount mount, const WristPlacement& wrist = {});
|
||||
// Move the center so the upper-left corner of the original panel remains
|
||||
// anchored when the same-aspect-ratio canvas changes physical width.
|
||||
Matrix34 resized_mount_pose(Matrix34 pose, float original_width, float scale, float aspect);
|
||||
|
||||
// Empty when neither XDG_CONFIG_HOME nor HOME specifies an absolute base.
|
||||
std::filesystem::path default_mount_settings_path();
|
||||
|
||||
+21
-6
@@ -76,6 +76,8 @@ struct Overlay::Impl {
|
||||
Panel panel;
|
||||
bool save_failed = false, debug_save_failed = false;
|
||||
bool world_ready = false, placed = false, has_texture = false, shown = false;
|
||||
float size_scale = 1.f;
|
||||
bool size_changed = false;
|
||||
vr::HmdMatrix34_t world_transform{};
|
||||
std::optional<Mount> applied_mount;
|
||||
vr::TrackedDeviceIndex_t anchor = vr::k_unTrackedDeviceIndexInvalid;
|
||||
@@ -211,21 +213,30 @@ struct Overlay::Impl {
|
||||
applied_mount.reset();
|
||||
}
|
||||
surface.set_placement_note(note);
|
||||
if (applied_mount != effective || (effective != Mount::World && anchor != target)) {
|
||||
surface.reset_pointers(); // a release on a relocated surface cannot activate an old press
|
||||
if (applied_mount != effective || (effective != Mount::World && anchor != target) || size_changed) {
|
||||
if (!size_changed || applied_mount != effective || anchor != target)
|
||||
surface.reset_pointers(); // a release on a relocated surface cannot activate an old press
|
||||
const float base_width = mount_width(effective, config.wrist);
|
||||
if (effective == Mount::World) {
|
||||
overlay_check(overlay->SetOverlayTransformAbsolute(handle, vr::TrackingUniverseStanding, &world_transform), overlay,
|
||||
Matrix34 pose{};
|
||||
for (int r = 0; r < 3; ++r) for (int c = 0; c < 4; ++c) pose[r][c] = world_transform.m[r][c];
|
||||
pose = resized_mount_pose(pose, base_width, size_scale, float(H) / W);
|
||||
vr::HmdMatrix34_t transform{};
|
||||
for (int r = 0; r < 3; ++r) for (int c = 0; c < 4; ++c) transform.m[r][c] = pose[r][c];
|
||||
overlay_check(overlay->SetOverlayTransformAbsolute(handle, vr::TrackingUniverseStanding, &transform), overlay,
|
||||
"SetOverlayTransformAbsolute");
|
||||
} else {
|
||||
const auto pose = relative_mount_pose(effective, config.wrist);
|
||||
const auto pose = resized_mount_pose(relative_mount_pose(effective, config.wrist),
|
||||
base_width, size_scale, float(H) / W);
|
||||
vr::HmdMatrix34_t transform{};
|
||||
for (int r = 0; r < 3; ++r) for (int c = 0; c < 4; ++c) transform.m[r][c] = pose[r][c];
|
||||
overlay_check(overlay->SetOverlayTransformTrackedDeviceRelative(handle, target, &transform), overlay,
|
||||
"SetOverlayTransformTrackedDeviceRelative");
|
||||
}
|
||||
overlay_check(overlay->SetOverlayWidthInMeters(handle, mount_width(effective, config.wrist)), overlay, "SetOverlayWidthInMeters");
|
||||
overlay_check(overlay->SetOverlayWidthInMeters(handle, base_width * size_scale), overlay, "SetOverlayWidthInMeters");
|
||||
applied_mount = effective;
|
||||
anchor = target;
|
||||
size_changed = false;
|
||||
}
|
||||
placed = true;
|
||||
visibility();
|
||||
@@ -331,7 +342,11 @@ struct Overlay::Impl {
|
||||
// a release must still hit the same enabled control.
|
||||
last_pointer_event = "overlay focus changed"; break;
|
||||
case vr::VREvent_MouseMove:
|
||||
surface.pointer_move(event.data.mouse.cursorIndex, event.data.mouse.x, H - event.data.mouse.y);
|
||||
if (auto factor = surface.pointer_move(event.data.mouse.cursorIndex, event.data.mouse.x,
|
||||
H - event.data.mouse.y)) {
|
||||
const float next = std::clamp(size_scale * *factor, .5f, 2.f);
|
||||
if (next != size_scale) { size_scale = next; size_changed = true; }
|
||||
}
|
||||
break;
|
||||
case vr::VREvent_MouseButtonDown:
|
||||
++pointer_downs;
|
||||
|
||||
+25
-3
@@ -13,6 +13,7 @@ namespace frameyap {
|
||||
namespace {
|
||||
constexpr int W = PanelSurface::width, H = PanelSurface::height;
|
||||
using Color = Rgba;
|
||||
constexpr int grip_x = 927, grip_y = 646, grip_w = 51, grip_h = 29;
|
||||
float rounded_distance(float px, float py, int x, int y, int w, int h, float radius) {
|
||||
const float dx = std::abs(px - (x + w / 2.f)) - (w / 2.f - radius);
|
||||
const float dy = std::abs(py - (y + h / 2.f)) - (h / 2.f - radius);
|
||||
@@ -109,6 +110,8 @@ struct PanelSurface::Impl {
|
||||
bool dirty = true, lasers_anytime = false, advanced_debug = false;
|
||||
std::string placement_note, binding_note;
|
||||
std::array<int, 2> pressed{{-1, -1}};
|
||||
int resize_cursor = -1;
|
||||
float resize_x = 0.f, resize_y = 0.f;
|
||||
std::vector<std::string> lines;
|
||||
size_t page = 0;
|
||||
static constexpr size_t lines_per_page = 4;
|
||||
@@ -262,6 +265,7 @@ struct PanelSurface::Impl {
|
||||
}
|
||||
void reset() {
|
||||
pressed.fill(-1);
|
||||
resize_cursor = -1;
|
||||
}
|
||||
bool render(const Panel& p) {
|
||||
if (p.recording != panel.recording || p.enabled != panel.enabled ||
|
||||
@@ -334,6 +338,11 @@ struct PanelSurface::Impl {
|
||||
active ? cyan : muted, b.r.x + b.r.w - 12);
|
||||
}
|
||||
}
|
||||
// Separate from the Quit hitbox. The three diagonal marks remain
|
||||
// visible on both tabs, slightly inset from the lower-right corner.
|
||||
for (int n = 0; n < 3; ++n)
|
||||
for (int i = 0; i < 13 + 5 * n; ++i)
|
||||
rect({956 - i, 657 + 5 * n + i / 3, 2, 2}, cyan);
|
||||
dirty = false;
|
||||
return true;
|
||||
}
|
||||
@@ -344,17 +353,30 @@ PanelSurface::~PanelSurface() = default;
|
||||
bool PanelSurface::render(const Panel& p) { return impl_->render(p); }
|
||||
const std::vector<unsigned char>& PanelSurface::pixels() const { return impl_->pixels; }
|
||||
bool PanelSurface::available(UiAction a) const { return impl_->available(a); }
|
||||
void PanelSurface::pointer_move(unsigned, float, float) {
|
||||
// Hit-test on down/up only; laser movement does not change panel content
|
||||
// and does not require a GPU texture upload.
|
||||
std::optional<float> PanelSurface::pointer_move(unsigned cursor, float x, float y) {
|
||||
if (cursor >= impl_->pressed.size() || int(cursor) != impl_->resize_cursor ||
|
||||
!std::isfinite(x) || !std::isfinite(y)) return {};
|
||||
// OpenVR supplies coordinates on the *current* panel. Work incrementally
|
||||
// so resizing never depends on a stale down-position after a width change.
|
||||
const float delta = (x - impl_->resize_x) / W - (y - impl_->resize_y) / H;
|
||||
impl_->resize_x = x; impl_->resize_y = y;
|
||||
return std::clamp(1.f + delta, .9f, 1.1f);
|
||||
}
|
||||
void PanelSurface::pointer_down(unsigned cursor, float x, float y) {
|
||||
if (cursor >= impl_->pressed.size()) return;
|
||||
if (impl_->resize_cursor < 0 && std::isfinite(x) && std::isfinite(y) &&
|
||||
x >= grip_x && x < grip_x + grip_w && y >= grip_y && y < grip_y + grip_h) {
|
||||
impl_->resize_cursor = int(cursor);
|
||||
impl_->resize_x = x; impl_->resize_y = y;
|
||||
impl_->pressed[cursor] = -1;
|
||||
return;
|
||||
}
|
||||
impl_->pressed[cursor] = impl_->hit(x, y);
|
||||
}
|
||||
SurfaceEvent PanelSurface::pointer_up(unsigned cursor, float x, float y) {
|
||||
SurfaceEvent result;
|
||||
if (cursor >= impl_->pressed.size()) return result;
|
||||
if (impl_->resize_cursor == int(cursor)) { impl_->resize_cursor = -1; return result; }
|
||||
int index = std::exchange(impl_->pressed[cursor], -1);
|
||||
if (index < 0 || impl_->hit(x, y) != index) return result;
|
||||
auto c = buttons[index].id;
|
||||
|
||||
@@ -28,7 +28,8 @@ public:
|
||||
const std::vector<unsigned char>& pixels() const;
|
||||
void pointer_down(unsigned cursor, float x, float y);
|
||||
SurfaceEvent pointer_up(unsigned cursor, float x, float y);
|
||||
void pointer_move(unsigned cursor, float x, float y);
|
||||
// Returns a relative physical-width change while dragging the resize grip.
|
||||
std::optional<float> pointer_move(unsigned cursor, float x, float y);
|
||||
void reset_pointers();
|
||||
void set_placement_note(std::string note);
|
||||
void set_lasers_anytime(bool enabled);
|
||||
|
||||
@@ -65,6 +65,19 @@ int main() {
|
||||
const auto head = relative_mount_pose(Mount::Head);
|
||||
check_basis(head, 1, 0, 0, 1);
|
||||
CHECK(near(head[0][3], 0)); CHECK(near(head[1][3], -.16f)); CHECK(near(head[2][3], -1.05f));
|
||||
for (auto mount : mounts) {
|
||||
const float width = mount_width(mount);
|
||||
const auto original = mount == Mount::World ? identity : relative_mount_pose(mount);
|
||||
const auto grown = resized_mount_pose(original, width, 1.5f, .68f);
|
||||
const auto shrunk = resized_mount_pose(original, width, .5f, .68f);
|
||||
CHECK(resized_mount_pose(original, width, 1.f, .68f) == original);
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
// Upper-left = center - width/2 * right + height/2 * up.
|
||||
const float old_corner = original[row][3] - width * .5f * original[row][0] + width * .34f * original[row][1];
|
||||
CHECK(near(grown[row][3] - width * .75f * grown[row][0] + width * .51f * grown[row][1], old_corner));
|
||||
CHECK(near(shrunk[row][3] - width * .25f * shrunk[row][0] + width * .17f * shrunk[row][1], old_corner));
|
||||
}
|
||||
}
|
||||
const auto left = relative_mount_pose(Mount::LeftWrist), right = relative_mount_pose(Mount::RightWrist);
|
||||
check_basis(right, 0, 1, -1, 0); // right wrist faces inward, not away from wearer
|
||||
for (int r = 0; r < 3; ++r) {
|
||||
|
||||
@@ -61,6 +61,19 @@ int main(int argc, char** argv) {
|
||||
no_action(click(surface, std::numeric_limits<float>::quiet_NaN(), 610));
|
||||
no_action(click(surface, -1, 610));
|
||||
no_action(click(surface, 1000, 680));
|
||||
// Lower-right grip scales without activating the nearby Quit button or
|
||||
// uploading new pixels. Other cursors cannot hijack an active drag.
|
||||
surface.pointer_down(0, 951, 656);
|
||||
assert(!surface.pointer_move(1, 970, 650));
|
||||
auto factor = surface.pointer_move(0, 971, 641);
|
||||
assert(factor && *factor > 1.f);
|
||||
no_action(surface.pointer_up(0, 900, 610));
|
||||
assert(!surface.pointer_move(0, 975, 640));
|
||||
assert(!surface.render(p));
|
||||
surface.pointer_down(0, 951, 656);
|
||||
surface.reset_pointers();
|
||||
assert(!surface.pointer_move(0, 971, 641));
|
||||
no_action(surface.pointer_up(0, 951, 656));
|
||||
|
||||
p.enabled = true;
|
||||
p.record_available = false;
|
||||
|
||||
Reference in new issue
Block a user