brep_render/engine_state/
camera_widgets.rs1use super::*;
2
3impl EngineState {
4 pub fn zoom_to_fit(&mut self) {
6 self.camera.zoom_to_fit(&self.scene.bbox(), 1.15);
7 self.dirty = true;
8 }
9
10 pub fn resize(&mut self, css_width: f64, css_height: f64) {
15 self.camera.width = css_width.max(1.0);
16 self.camera.height = css_height.max(1.0);
17 self.dirty = true;
18 }
19
20 pub fn pointer_down(&mut self, x: f64, y: f64, button: i32) -> bool {
23 if self.sketch_mode()
29 && self.sketch_camera_locked
30 && button == crate::controls::BUTTON_LEFT
31 {
32 return false;
33 }
34 self.controls.pointer_down(x, y, button)
35 }
36
37 pub fn pointer_move(&mut self, x: f64, y: f64) -> bool {
38 let changed = self.controls.pointer_move(&mut self.camera, x, y);
39 if changed {
40 self.dirty = true;
41 }
42 changed
43 }
44
45 pub fn pointer_up(&mut self) -> bool {
46 self.controls.pointer_up()
47 }
48
49 pub fn wheel(&mut self, delta_y: f64, cursor: Option<[f64; 2]>) -> bool {
50 let changed = self.controls.wheel(&mut self.camera, delta_y, cursor);
51 if changed {
52 self.dirty = true;
53 }
54 changed
55 }
56
57 pub fn set_controls_enabled(&mut self, enabled: bool) {
58 self.controls.enabled = enabled;
59 }
60
61 pub fn toggle_projection(&mut self) -> &'static str {
64 let kind = self.camera.toggle_projection();
65 self.dirty = true;
66 kind
67 }
68
69 pub fn set_projection(&mut self, kind: &str) {
70 let is_persp = matches!(self.camera.projection, crate::view::Projection::Perspective { .. });
71 let want_persp = kind.to_ascii_lowercase().starts_with("pers");
72 if is_persp != want_persp {
73 self.camera.toggle_projection();
74 self.dirty = true;
75 }
76 }
77
78 pub fn standard_view(&mut self, name: &str) -> bool {
79 let ok = self.camera.standard_view(name);
80 if ok {
81 self.camera.zoom_to_fit(&self.scene.bbox(), 1.15);
82 self.dirty = true;
83 }
84 ok
85 }
86
87 pub fn camera_state_json(&self) -> String {
88 self.camera.state_json()
89 }
90
91 pub fn apply_camera_state_json(&mut self, json: &str) -> Result<(), String> {
92 self.camera.apply_state_json(json)?;
93 self.dirty = true;
94 Ok(())
95 }
96
97 pub fn world_per_pixel(&self) -> f64 {
98 self.camera.world_per_pixel()
99 }
100
101 pub fn world_to_screen_json(&self, points_json: &str) -> Result<String, String> {
107 let points: Vec<[f64; 3]> = serde_json::from_str(points_json)
108 .map_err(|error| format!("world_to_screen points parse: {error}"))?;
109 let out: Vec<[f64; 4]> = points
110 .into_iter()
111 .map(|p| {
112 let (sx, sy, depth) = self.camera.project(p);
113 [sx, sy, depth, if depth > 0.0 { 1.0 } else { 0.0 }]
114 })
115 .collect();
116 Ok(serde_json::to_string(&out).unwrap_or_else(|_| "[]".to_string()))
117 }
118
119 pub fn camera_matrices_json(&self) -> String {
127 let view_proj = self.camera.view_proj_flat();
128 let view_proj_inverse = self.camera.view_proj_inverse_flat();
129 serde_json::json!({
130 "viewProj": view_proj,
131 "viewProjInverse": view_proj_inverse,
132 "viewport": [self.camera.width, self.camera.height],
133 })
134 .to_string()
135 }
136
137 }
140
141impl EngineState {
142 pub fn build_widget_overlay(&self) -> Option<WidgetOverlay> {
145 if !self.widgets.any_visible() {
146 return None;
147 }
148 Some(self.widgets.build_overlay(&gizmo_camera(&self.camera)))
149 }
150
151 pub fn set_datums_json(&mut self, json: &str) -> Result<(), String> {
152 self.widgets.set_datums_json(json)?;
153 self.dirty = true;
154 Ok(())
155 }
156
157 pub fn set_overlay_json(&mut self, json: &str) -> Result<(), String> {
161 self.widgets.set_overlay_json(json)?;
162 self.dirty = true;
163 Ok(())
164 }
165
166 pub fn set_dimensions_json(&mut self, json: &str) -> Result<(), String> {
167 self.widgets.set_dimensions_json(json)?;
168 self.dirty = true;
169 Ok(())
170 }
171
172 pub fn set_transform_json(&mut self, json: &str) -> Result<(), String> {
173 self.widgets.set_transform_json(json)?;
174 self.dirty = true;
175 Ok(())
176 }
177
178 pub fn set_viewcube_enabled(&mut self, enabled: bool) {
179 self.widgets.set_viewcube_enabled(enabled);
180 self.dirty = true;
181 }
182
183 pub fn viewcube_rect_json(&self) -> String {
186 let r = self.widgets.viewcube_rect(&gizmo_camera(&self.camera));
187 serde_json::json!({ "x": r[0], "y": r[1], "w": r[2], "h": r[3] }).to_string()
188 }
189
190 pub fn viewcube_hover(&mut self, local_x: f64, local_y: f64) -> bool {
193 let cam = gizmo_camera(&self.camera);
194 let handle = self.widgets.viewcube_hit(&cam, local_x as f32, local_y as f32);
195 let changed = self.widgets.set_viewcube_hover(handle);
196 if changed {
197 self.dirty = true;
198 }
199 changed
200 }
201
202 pub fn viewcube_clear_hover(&mut self) -> bool {
203 let changed = self.widgets.set_viewcube_hover(None);
204 if changed {
205 self.dirty = true;
206 }
207 changed
208 }
209
210 pub fn viewcube_click(&mut self, local_x: f64, local_y: f64) -> bool {
214 let cam = gizmo_camera(&self.camera);
215 let Some(handle) = self.widgets.viewcube_hit(&cam, local_x as f32, local_y as f32) else {
216 return false;
217 };
218 if brep_gizmos::view_cube::ViewCube::is_arrow(handle) {
221 self.apply_viewcube_arrow(handle);
222 self.dirty = true;
223 return true;
224 }
225 let (dir, fallback_up) = self.widgets.viewcube_target(handle);
226 let dirf = [dir[0] as f64, dir[1] as f64, dir[2] as f64];
231 let cu = self.camera.up;
232 let d = cu[0] * dirf[0] + cu[1] * dirf[1] + cu[2] * dirf[2];
233 let proj = [
234 cu[0] - dirf[0] * d,
235 cu[1] - dirf[1] * d,
236 cu[2] - dirf[2] * d,
237 ];
238 let len = (proj[0] * proj[0] + proj[1] * proj[1] + proj[2] * proj[2]).sqrt();
239 let up = if len > 1e-4 {
240 [
241 (proj[0] / len) as f32,
242 (proj[1] / len) as f32,
243 (proj[2] / len) as f32,
244 ]
245 } else {
246 fallback_up
247 };
248 self.apply_look_direction(dir, up);
249 self.dirty = true;
250 true
251 }
252
253 pub(super) fn apply_look_direction(&mut self, dir: [f32; 3], up: [f32; 3]) {
256 let dir = [dir[0] as f64, dir[1] as f64, dir[2] as f64];
257 let dist = self.camera.distance();
258 self.camera.eye = [
259 self.camera.target[0] - dir[0] * dist,
260 self.camera.target[1] - dir[1] * dist,
261 self.camera.target[2] - dir[2] * dist,
262 ];
263 self.camera.up = [up[0] as f64, up[1] as f64, up[2] as f64];
264 }
265
266 fn apply_viewcube_arrow(&mut self, handle: u32) {
272 use crate::view::{add3, rotate3, sub3};
273 use brep_gizmos::view_cube::ViewCube;
274 let (right, up_axis, fwd) = self.camera.basis();
276 let target = self.camera.target;
277 let rel = sub3(self.camera.eye, target); let q = std::f64::consts::FRAC_PI_2; match handle {
280 ViewCube::ARROW_RIGHT => {
282 self.camera.eye = add3(target, rotate3(rel, up_axis, q));
283 }
284 ViewCube::ARROW_LEFT => {
285 self.camera.eye = add3(target, rotate3(rel, up_axis, -q));
286 }
287 ViewCube::ARROW_UP => {
290 self.camera.eye = add3(target, rotate3(rel, right, -q));
291 self.camera.up = rotate3(self.camera.up, right, -q);
292 }
293 ViewCube::ARROW_DOWN => {
294 self.camera.eye = add3(target, rotate3(rel, right, q));
295 self.camera.up = rotate3(self.camera.up, right, q);
296 }
297 ViewCube::ROLL_CCW => {
299 self.camera.up = rotate3(self.camera.up, fwd, q);
300 }
301 ViewCube::ROLL_CW => {
302 self.camera.up = rotate3(self.camera.up, fwd, -q);
303 }
304 _ => {}
305 }
306 }
307
308 pub fn datum_pick(&self, x: f64, y: f64) -> String {
311 self.widgets
312 .datum_pick(&gizmo_camera(&self.camera), x as f32, y as f32)
313 .unwrap_or_default()
314 }
315
316 pub fn transform_hover(&mut self, x: f64, y: f64) -> u32 {
319 let cam = gizmo_camera(&self.camera);
320 let handle = self.widgets.transform_hit(&cam, x as f32, y as f32);
321 if self.widgets.set_transform_hover(handle) {
322 self.dirty = true;
323 }
324 handle
325 }
326
327 pub fn transform_pick(&self, x: f64, y: f64) -> u32 {
330 self.widgets.transform_hit(&gizmo_camera(&self.camera), x as f32, y as f32)
331 }
332
333 pub fn transform_drag(
336 &mut self,
337 handle: u32,
338 sx: f64,
339 sy: f64,
340 cx: f64,
341 cy: f64,
342 ) -> String {
343 let cam = gizmo_camera(&self.camera);
344 self.widgets.set_transform_active(handle);
345 self.dirty = true;
346 self.widgets
347 .transform_drag_json(&cam, handle, sx as f32, sy as f32, cx as f32, cy as f32)
348 }
349
350 pub fn transform_drag_end(&mut self) {
351 self.widgets.set_transform_active(0);
352 self.dirty = true;
353 }
354
355 pub fn dimension_anchors_json(&self) -> String {
358 let anchors = self.widgets.dimension_anchors(&gizmo_camera(&self.camera));
359 let out: Vec<serde_json::Value> = anchors
360 .into_iter()
361 .map(|(id, p)| {
362 let (sx, sy, depth) = self.camera.project([p[0] as f64, p[1] as f64, p[2] as f64]);
363 serde_json::json!({
364 "id": id,
365 "anchor": p,
366 "screen": [sx, sy, if depth > 0.0 { 1.0 } else { 0.0 }],
367 })
368 })
369 .collect();
370 serde_json::to_string(&out).unwrap_or_else(|_| "[]".to_string())
371 }
372
373 }