feat: view.apply restores a named view or camera; MCP screenshot takes a named view or camera (#528) (#595)
New view.apply command restores a named view, a standard view or any camera (view state, not an undo step); the desktop viewport follows it and the browser's Restore uses it. The MCP screenshot tool takes a named view or a camera. Un-ignores the #528 regression test. Fixes #528. Co-authored-by: jonaswre <[email protected]>
This commit is contained in:
1 parent
e67cc0c747
commit
27f20f97cb
11 files changed
+159
-23
No files matched your search
@@ -862,10 +862,8 @@ pub fn restore_view(app: &mut SolveApp, name: &str) {
|
||||
app.animate_view(v);
|
||||
return;
|
||||
}
|
||||
let Some(v) = app.session.doc.named_views.iter().find(|v| v.name == name) else { return };
|
||||
let j = json!({ "target": v.target, "yaw": v.yaw, "pitch": v.pitch, "distance": v.distance, "fov": v.fov });
|
||||
if let Ok(cam) = serde_json::from_value::<solidcraft_engine::render::Camera>(j) {
|
||||
app.animate_to(cam);
|
||||
if app.run("view.apply", json!({ "view": name })).is_ok() {
|
||||
app.follow_view_request(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ pub fn handle(app: &mut SolveApp, ctx: &egui::Context, req: &ControlRequest) ->
|
||||
if r.is_ok() && app.session.active_sketch.is_none() && app.session.world_state().bodies.len() == 1 && app.session.undo.len() <= 2 {
|
||||
app.fit_view();
|
||||
}
|
||||
// After the first-body fit, so a camera the command asked for (`view.apply`) wins.
|
||||
app.follow_view_request(false);
|
||||
wrap(r)
|
||||
}
|
||||
"ui.agent_cursor" => {
|
||||
@@ -116,7 +118,11 @@ pub fn handle(app: &mut SolveApp, ctx: &egui::Context, req: &ControlRequest) ->
|
||||
}
|
||||
ok(json!(app.ui.agent_cursor))
|
||||
}
|
||||
"engine.script" => wrap(app.session.run_script(p).map(|v| json!(v)).map_err(|e| e.to_string())),
|
||||
"engine.script" => {
|
||||
let r = app.session.run_script(p).map(|v| json!(v)).map_err(|e| e.to_string());
|
||||
app.follow_view_request(false);
|
||||
wrap(r)
|
||||
}
|
||||
"engine.commands" => wrap(app.session.execute("engine.commands", &json!({})).map_err(|e| e.to_string())),
|
||||
"document.inspect" => wrap(app.session.execute("document.inspect", p).map_err(|e| e.to_string())),
|
||||
"ui.inspect" => ok(json!({
|
||||
|
||||
@@ -884,6 +884,21 @@ impl SolveApp {
|
||||
self.pending_fit = request;
|
||||
}
|
||||
|
||||
/// Move the viewport to the camera a command asked for (`view.apply`): animated like a view
|
||||
/// change, or at once (control requests, so a screenshot right after shows it).
|
||||
pub fn follow_view_request(&mut self, animate: bool) {
|
||||
let Some(mut to) = self.session.camera.take() else { return };
|
||||
to.up = self.cam.up;
|
||||
if animate {
|
||||
self.animate_to(to);
|
||||
} else if to.is_valid() {
|
||||
self.cancel_view_animation();
|
||||
self.viewport.orbit_snap = None;
|
||||
self.viewport.orbit_free = None;
|
||||
self.cam = to;
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop any view animation where it is (the user took over the camera).
|
||||
pub fn cancel_view_animation(&mut self) {
|
||||
self.pending_fit = None;
|
||||
@@ -930,6 +945,7 @@ impl SolveApp {
|
||||
}
|
||||
self.step_view_animation(ctx);
|
||||
self.drain_control(ctx);
|
||||
self.follow_view_request(true);
|
||||
agent_cursor::step(self, ctx);
|
||||
if !self.synthetic.is_empty() {
|
||||
ctx.request_repaint();
|
||||
|
||||
Reference in new issue
Block a user