geop_ops_assembly/
panel.rs1use std::{
6 cell::RefCell,
7 collections::{BTreeMap, BTreeSet},
8 rc::Rc,
9};
10
11use geop_core_editor::{
12 editor::{Changed, EditorContext},
13 registry::Panel,
14};
15use geop_core_math::{
16 geop_error::{GeopError, GeopResult},
17 scalars::Scalar,
18};
19use geop_core_program::{program::Program, reader::Reader};
20use serde::{Deserialize, Serialize};
21
22use geop_ops_model::placed::placed_parts;
23
24use crate::{
25 settle::Settled,
26 solving::{self, Freedom, SettingsState, settings},
27};
28
29pub struct JointsPanel {
31 pub(crate) settled: Rc<Settled>,
32 freedom: RefCell<Option<Freedom>>,
36}
37
38impl JointsPanel {
39 pub(crate) fn new(settled: Rc<Settled>) -> Self {
40 Self {
41 settled,
42 freedom: RefCell::new(None),
43 }
44 }
45}
46
47#[derive(Serialize)]
49pub struct JointsState {
50 pub joints: Vec<SettingsState>,
54 pub freedom: Option<Freedom>,
57 pub conflicting: Vec<String>,
60}
61
62#[derive(Serialize, Deserialize)]
64#[serde(tag = "command", rename_all = "snake_case")]
65pub enum JointsCommand {
66 Set { parameter: String, value: f64 },
70}
71
72impl Panel for JointsPanel {
73 const KEY: &'static str = "joints";
74 type State = JointsState;
75 type Command = JointsCommand;
76
77 fn state(&self, _: &Program, part: &Reader) -> JointsState {
78 let placed = placed_parts(part).is_ok_and(|placed| !placed.is_empty());
79 JointsState {
80 joints: settings(part),
81 freedom: self.freedom.borrow().clone().filter(|_| placed),
82 conflicting: self.settled.conflicting.borrow().clone(),
83 }
84 }
85
86 fn details(&self, state: &JointsState) -> BTreeMap<String, String> {
88 let parts = state.freedom.iter().flat_map(|f| &f.parts);
89 (parts.map(|(part, &dof)| {
90 let said = if dof == 0 {
91 "fixed".into()
92 } else {
93 format!("{dof} DOF")
94 };
95 (part.clone(), said)
96 }))
97 .collect()
98 }
99
100 fn command(&self, ctx: &mut EditorContext, command: JointsCommand) -> GeopResult<Changed> {
101 let JointsCommand::Set { parameter, value } = command;
102 ctx.idle()?;
103 ctx.run_all();
104 let settable = (settings(&ctx.part()).into_iter())
105 .any(|s| s.values.iter().any(|v| v.state == parameter));
106 if !settable {
107 return Err(GeopError::new(format!(
108 "{parameter:?} is no value of this program a user sets"
109 )));
110 }
111 let set = BTreeMap::from([(parameter, Scalar::from_f64(value))]);
112 let mut rerun = ctx.rerun();
113 let state = rerun.program.state.clone();
114 let (moved, _) = solving::set(&mut rerun, &state, &set)?;
115 ctx.move_state(moved);
116 Ok(Changed::Program)
117 }
118
119 fn after_edit(&self, ctx: &mut EditorContext) {
122 let placed = placed_parts(&ctx.part()).is_ok_and(|placed| !placed.is_empty());
123 let mut rerun = ctx.rerun();
124 let state = rerun.program.state.clone();
125 *self.freedom.borrow_mut() = placed
126 .then(|| solving::freedom(&mut rerun, &state, &BTreeSet::new()).ok())
127 .flatten();
128 }
129}