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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
use std::cell::Cell;
use cranpose_core::NodeId;
use cranpose_ui_graphics::{
Point, Size,
space::{Camera, Mat4, MeshSurface, Ray, SurfaceHit, Vec3},
};
use crate::{
graph::{LayerNode, ProjectiveTransform},
space::{EyeView, SpaceTree, node_size},
};
/// How much past a doorway a ray that goes through it looks for what lies
/// beyond, against its distance: the surface the doorway lies on is not
/// beyond it.
const PAST_DOORWAY: f32 = 1.0e-4;
/// Finds which mesh surface of a scene a window point, or any ray, shows,
/// and the point of the surface's content there: the nearest surface along
/// the ray in the space the window's eye stands in, or behind a doorway the
/// ray meets first, in the spaces the eye sees.
pub struct ScenePick {
/// The node of the scene the window shows.
scene: Option<NodeId>,
camera: Camera,
size: Size,
window_to_scene: ProjectiveTransform,
scene_to_window: ProjectiveTransform,
surfaces: Vec<PickSurface>,
seen: Vec<PickSpace>,
/// The scene nodes of every space of the scene, seen or not.
scenes: Vec<NodeId>,
/// The scene nodes of the spaces the eye sees.
seen_scenes: Vec<NodeId>,
/// The last window point picked, and what it met.
last: Cell<Option<LastPick>>,
}
/// A space the eye sees, as the pick goes into it: the seen space whose
/// doorway it shows through, and the map onto that doorway's unit square.
struct PickSpace {
through: Option<usize>,
to_door: Mat4,
}
impl PickSpace {
/// How far along `ray` it passes through the doorway, if it does.
fn meet(&self, ray: Ray) -> Option<f32> {
let origin = self.to_door.transform_point(ray.origin);
let direction = self.to_door.transform_vector(ray.direction);
if direction.z.abs() < f32::EPSILON {
return None;
}
let distance = -origin.z / direction.z;
let at = origin + direction * distance;
let inside = (0.0..=1.0).contains(&at.x) && (0.0..=1.0).contains(&at.y);
(inside && distance > 0.0).then_some(distance)
}
}
/// A window point and the surface it met, if any, with where.
type LastPick = (Point, Option<(usize, SurfaceHit)>);
struct PickSurface {
node_id: Option<NodeId>,
surface: MeshSurface,
size: Size,
/// The seen space the surface lies in.
seen: usize,
/// The triangle the last ray that met this surface met, on whose plane
/// a drag that leaves the mesh goes on.
held: Cell<Option<u32>>,
}
impl ScenePick {
/// The pick of the scene that `layer` shows, with `to_window` mapping
/// the layer to the window; `None` when the layer shows no scene. Its
/// surfaces are those the window's eye sees.
pub fn of(layer: &LayerNode, to_window: ProjectiveTransform) -> Option<Self> {
let window_to_scene = to_window.inverse()?;
let tree = SpaceTree::of(layer)?;
let view = EyeView::of(&tree, node_size(layer));
let seen_scenes = view
.seen
.iter()
.filter_map(|seen| tree.cells[seen.cell].layer.node_id)
.collect();
let seen = view
.seen
.iter()
.map(|space| PickSpace {
through: space.through,
to_door: space
.door
.and_then(|door| door.quad.inverse())
.unwrap_or(Mat4::IDENTITY),
})
.collect();
let surfaces = view
.surfaces
.into_iter()
.map(|seen| PickSurface {
node_id: seen.layer.node_id,
surface: seen.surface,
size: seen.size,
seen: seen.seen,
held: Cell::new(None),
})
.collect();
Some(Self {
scene: layer.node_id,
camera: view.camera,
size: view.size,
window_to_scene,
scene_to_window: to_window,
surfaces,
seen,
scenes: tree
.cells
.iter()
.filter_map(|cell| cell.layer.node_id)
.collect(),
seen_scenes,
last: Cell::new(None),
})
}
/// The node of the scene the window shows.
pub fn scene(&self) -> Option<NodeId> {
self.scene
}
/// Whether `layer` shows one of this scene's spaces, a scene behind one
/// of its doorways, whose surfaces this pick finds.
pub fn holds_scene(&self, layer: &LayerNode) -> bool {
layer
.node_id
.is_some_and(|node_id| self.scenes.contains(&node_id))
}
/// Whether the eye sees the space of the scene `layer` shows, through
/// the doorways before it.
pub fn sees_scene(&self, layer: &LayerNode) -> bool {
layer
.node_id
.is_some_and(|node_id| self.seen_scenes.contains(&node_id))
}
/// The index among the scene's surfaces of the surface layer `layer`.
pub fn index_of(&self, layer: &LayerNode) -> Option<usize> {
let node_id = layer.node_id?;
self.surfaces
.iter()
.position(|surface| surface.node_id == Some(node_id))
}
/// The content point of surface `index` that the window point shows,
/// when that surface is the nearest one there.
pub fn content_point(&self, window: Point, index: usize) -> Option<Point> {
let (nearest, hit) = self.nearest(window)?;
(nearest == index).then_some(hit.point)
}
/// The surface `ray`, in the space the window's eye stands in, meets
/// first, by its index, and where.
pub fn nearest_along(&self, ray: Ray) -> Option<(usize, SurfaceHit)> {
self.nearest_in(ray, 0, f32::NEG_INFINITY)
}
/// The content point of surface `index` under the window point for a
/// gesture that surface holds: where the ray meets it, or past its edge,
/// where the ray meets the plane of the triangle it last met.
pub fn drag_point(&self, window: Point, index: usize) -> Option<Point> {
self.drag_point_along(self.ray(window)?, index)
}
/// [`Self::drag_point`] for a ray in the space the window's eye stands
/// in.
pub fn drag_point_along(&self, ray: Ray, index: usize) -> Option<Point> {
let target = self.surfaces.get(index)?;
if let Some(hit) = target.surface.cast(ray, target.size) {
target.held.set(Some(hit.triangle));
return Some(hit.point);
}
target
.surface
.cast_plane(ray, target.held.get()?, target.size)
}
/// Where the content point `content` of surface `index` shows in the
/// window, also for a surface behind a doorway; `None` when it lies
/// nearer than the camera's near plane or behind the eye.
pub fn window_point(&self, index: usize, content: Point) -> Option<Point> {
let target = self.surfaces.get(index)?;
let at = target.surface.point_at(content, target.size)?;
let shown = self.camera.project(at, self.size)?;
Some(self.scene_to_window.map_point(shown))
}
/// Where `point`, in the space the window's eye stands in, shows in the
/// window; `None` behind the eye.
pub fn window_point_of(&self, point: Vec3) -> Option<Point> {
let shown = self.camera.project(point, self.size)?;
Some(self.scene_to_window.map_point(shown))
}
fn ray(&self, window: Point) -> Option<Ray> {
let local = self.window_to_scene.map_point(window);
self.camera.ray(local, self.size)
}
fn nearest(&self, window: Point) -> Option<(usize, SurfaceHit)> {
if let Some((point, nearest)) = self.last.get()
&& point == window
{
return nearest;
}
let nearest = self.ray(window).and_then(|ray| self.nearest_along(ray));
if let Some((index, hit)) = nearest {
self.surfaces[index].held.set(Some(hit.triangle));
}
self.last.set(Some((window, nearest)));
nearest
}
/// The nearest surface of seen space `seen` that `ray` meets past
/// `beyond`; where it first passes through the doorway of a space seen
/// through this one, the nearest surface on the doorway's other side,
/// or this space's own where that side shows none.
fn nearest_in(&self, ray: Ray, seen: usize, beyond: f32) -> Option<(usize, SurfaceHit)> {
let surface = self
.surfaces
.iter()
.enumerate()
.filter(|(_, target)| target.seen == seen)
.filter_map(|(index, target)| {
target
.surface
.cast(ray, target.size)
.filter(|hit| hit.distance > beyond)
.map(|hit| (index, hit))
})
.min_by(|(_, a), (_, b)| a.distance.total_cmp(&b.distance));
let doorway = self
.seen
.iter()
.enumerate()
.filter(|(_, space)| space.through == Some(seen))
.filter_map(|(index, space)| {
space
.meet(ray)
.filter(|distance| *distance > beyond)
.map(|distance| (index, distance))
})
.min_by(|(_, a), (_, b)| a.total_cmp(b));
let Some((door, distance)) = doorway.filter(|(_, distance)| {
surface.is_none_or(|(_, hit)| *distance <= hit.distance * (1.0 + PAST_DOORWAY))
}) else {
return surface;
};
self.nearest_in(ray, door, distance * (1.0 + PAST_DOORWAY))
.or(surface)
}
}
/// The mesh surface layer a child of a scene layer is, or holds as the
/// wrapper of its outer draws.
pub fn surface_layer(child: &LayerNode) -> Option<&LayerNode> {
let layer = match child.wraps {
None => child,
Some(node_id) => child.children.iter().find_map(|inner| match inner {
crate::graph::RenderNode::Layer(inner) if inner.node_id == Some(node_id) => {
Some(&**inner)
}
_ => None,
})?,
};
cranpose_ui_graphics::layer_transform::layer_surface(&layer.graphics_layer).map(|_| layer)
}