1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
--- a/plugins/cube/cube.cpp
+++ b/plugins/cube/cube.cpp
@@ -493,17 +493,14 @@
std::max(1.0f, float(geometry.height));
}
- float cube_front_extent(float yaw)
- {
- float extent = 0;
- for (int face = 0; face < get_num_faces(); face++)
- {
- const float angle = face * animation.side_angle + yaw;
- extent = std::max(extent, identity_z_offset * std::cos(angle) +
- geometry_aspect * 0.5f * std::abs(std::sin(angle)));
- }
-
- return extent;
+ float cube_rotation_radius()
+ {
+ // A pose-independent envelope about the geometric center. It keeps
+ // wide faces clear of the camera without following individual corners.
+ const bool stack = (mouse_overview_active || render_at_rest) && !vertical_transition;
+ const float rows = stack ? output->wset()->get_workspace_grid_size().height : 1;
+ return std::sqrt(identity_z_offset * identity_z_offset +
+ geometry_aspect * geometry_aspect * 0.25f + rows * rows * 0.25f);
}
bool refresh_cube_geometry()
@@ -1290,7 +1287,7 @@
animation.cube_animation.rotation.set(current_rotation, current_rotation);
animation.cube_animation.offset_y.set(current_offset_y, current_offset_y);
animation.cube_animation.offset_z.restart_with_end(
- zoom_opt + identity_z_offset + front_face_distance());
+ zoom_opt + cube_rotation_radius() + front_face_distance());
animation.cube_animation.zoom.set(current_zoom, current_zoom);
animation.cube_animation.ease_deformation.restart_with_end(1);
@@ -1370,7 +1367,7 @@
}
/* Update the view matrix used in the next frame */
- glm::mat4 cube_view_matrix(float zoom, bool nominal_camera = false)
+ glm::mat4 cube_view_matrix(float zoom)
{
const float pitch = animation.cube_animation.offset_y;
const bool stack = (mouse_overview_active || render_at_rest) && !vertical_transition;
@@ -1378,29 +1375,16 @@
const float row = mouse_overview_active ? float(overview_row) :
output->wset()->get_current_workspace().y;
const float center = stack ? ((rows - 1) / 2 - row) / zoom : 0;
- // Keep the nearest corner in front of the camera as the regular
- // polygon turns. Anchor zoom to that surface, rather than scaling
- // a wide cube through a stationary camera.
- const float support = std::cos(pitch) *
- cube_front_extent(animation.cube_animation.rotation) +
- std::abs(std::sin(pitch)) * rows * 0.5f;
- const float base = nominal_camera ? identity_z_offset + front_face_distance() :
- float(animation.cube_animation.offset_z);
- const float distance = base + support / zoom - identity_z_offset;
- auto zoom_translate = glm::translate(glm::mat4(1.f),
+ // Rotation must not move the camera. Only entry/exit and explicit
+ // zoom change its distance; yaw and pitch orbit the same cube center.
+ const float distance = float(animation.cube_animation.offset_z) +
+ cube_rotation_radius() * (1.0f / zoom - 1.0f);
+ auto camera = glm::translate(glm::mat4(1.f),
glm::vec3(0.f, 0.f, -distance));
-
- auto rotation = glm::rotate(glm::mat4(1.0),
- (float)animation.cube_animation.offset_y,
- glm::vec3(1., 0., 0.));
-
- auto view = glm::lookAt(glm::vec3(0., 0., 0.),
- glm::vec3(0., 0., -animation.cube_animation.offset_z),
- glm::vec3(0., 1., 0.));
-
- auto pivot = glm::translate(glm::mat4(1.0), glm::vec3(0, center, 0));
- auto unpivot = glm::translate(glm::mat4(1.0), glm::vec3(0, -center, 0));
- return zoom_translate * pivot * rotation * unpivot * view;
+ auto rotation = glm::rotate(glm::mat4(1.f), pitch, glm::vec3(1, 0, 0));
+ auto pivot = glm::translate(glm::mat4(1.f), glm::vec3(0, center, 0));
+ auto unpivot = glm::translate(glm::mat4(1.f), glm::vec3(0, -center, 0));
+ return camera * pivot * rotation * unpivot;
}
void update_view_matrix()
@@ -1410,32 +1394,18 @@
float overview_fit()
{
- // Measure the projected outline with neutral wheel zoom and camera
- // distance. Fitting model dimensions again would double-count the
- // camera's pullback, making tilted wide desktops unnecessarily tiny.
- // User zoom and camera distance remain free to change the fitted size.
- auto vp = animation.projection * cube_view_matrix(1.0f, true);
- const float last = output->wset()->get_workspace_grid_size().height - 1;
- float extent = 1.0f;
- for (float row : {0.0f, last})
- {
- for (int face = 0; face < get_num_faces(); face++)
- {
- auto matrix = vp * calculate_model_matrix(face, row - float(overview_row));
- for (float x : {-0.5f, 0.5f})
- {
- for (float y : {-0.5f, 0.5f})
- {
- auto corner = matrix * glm::vec4(x, y, 0, 1);
- const float w = std::max(1e-3f, corner.w);
- extent = std::max(extent, std::max(std::abs(corner.x),
- std::abs(corner.y)) / w);
- }
- }
- }
- }
-
- return 0.8f / extent;
+ // Fit the rotation-invariant envelope once, rather than refitting each
+ // pose. Otherwise its changing outline creates a second zoom pulse.
+ // Wheel zoom and the configured camera offset stay independent of fit.
+ const float radius = cube_rotation_radius();
+ const float distance = radius + front_face_distance();
+ const float rows = output->wset()->get_workspace_grid_size().height;
+ const float center = std::abs((rows - 1) / 2 - float(overview_row));
+ const float tangent = distance * distance - radius * radius;
+ const float horizontal = animation.projection[0][0] * radius / std::sqrt(tangent);
+ const float vertical = animation.projection[1][1] *
+ (center * distance + radius * std::sqrt(tangent + center * center)) / tangent;
+ return 0.8f / std::max({1.0f, horizontal, vertical});
}
glm::mat4 output_transform(const wf::render_target_t& target)
|