Skip to main content

geop_core_program/
values.rs

1//! The values a program is run with from outside: what a program placing
2//! it sets its parameters to ([`Setting`]), and what its state holds
3//! ([`StateValue`]): where its placed parts are and how its joints are
4//! turned — what a solve moves, kept with the program between runs. Both
5//! are saved in a program file, so they are the file format's, not an
6//! operation's.
7
8use std::collections::BTreeMap;
9
10use geop_core_math::{
11    geop_error::{GeopError, GeopResult},
12    primitives::Pose,
13    scalars::{Scalar, as_f64},
14    solvers::{least_squares::Rows, system::Variable},
15};
16use serde::{Deserialize, Serialize};
17
18/// What a parameter is set to where a program places the part: a number,
19/// or the name of a table's row, or a colour. Saved as itself — `2.5`,
20/// `"M5"`, `"#ff8800"`.
21#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22#[serde(untagged)]
23pub enum Setting {
24    Number(f64),
25    Text(String),
26}
27
28/// Parameter settings, by name: the inputs of a run.
29pub type Settings = BTreeMap<String, Setting>;
30
31/// A value of a part's state: a number or a pose. Serialized as itself —
32/// `2.5`, `{"position": [...], "rotation": [...]}`.
33#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum StateValue {
36    Number(#[serde(with = "as_f64")] Scalar),
37    Pose(Pose),
38}
39
40/// A part's state, by name.
41pub type StateValues = BTreeMap<String, StateValue>;
42
43/// A value the run gives none of reads as the number zero.
44impl Default for StateValue {
45    fn default() -> Self {
46        StateValue::Number(Scalar::ZERO)
47    }
48}
49
50/// The name of the state that is where the part placed as `instance` is:
51/// `bolt.pose`.
52pub fn pose_state(instance: &str) -> String {
53    format!("{instance}.pose")
54}
55
56/// The pose a value holds, if it holds one: none for a number — what a
57/// pose state reads as before anything solved for it.
58pub fn pose_in(value: &StateValue) -> Option<Pose> {
59    match value {
60        StateValue::Pose(pose) => Some(*pose),
61        StateValue::Number(_) => None,
62    }
63}
64
65/// A number steps by what is added to it; a pose by a twist `(ω, v)` in its
66/// own frame — turning about its own origin, and moving along its own axes —
67/// by the exponential map: `pose ∘ exp(ω, v)` (see [`Pose`]'s steps).
68impl Variable for StateValue {
69    fn dimension(&self) -> usize {
70        match self {
71            StateValue::Number(v) => v.dimension(),
72            StateValue::Pose(pose) => pose.dimension(),
73        }
74    }
75
76    fn retract(&self, step: &[Scalar]) -> GeopResult<Self> {
77        Ok(match self {
78            StateValue::Number(v) => StateValue::Number(v.retract(step)?),
79            StateValue::Pose(pose) => StateValue::Pose(pose.retract(step)?),
80        })
81    }
82
83    fn retract_over(&self, step: &[Scalar]) -> GeopResult<Self> {
84        Ok(match self {
85            StateValue::Number(v) => StateValue::Number(v.retract_over(step)?),
86            StateValue::Pose(pose) => StateValue::Pose(pose.retract_over(step)?),
87        })
88    }
89
90    fn differential(&self, step: &[Scalar]) -> GeopResult<Option<Vec<Vec<Scalar>>>> {
91        match self {
92            StateValue::Number(v) => v.differential(step),
93            StateValue::Pose(pose) => pose.differential(step),
94        }
95    }
96
97    /// A number its difference from `at`; a pose its position's, and its
98    /// rotation's — a turn as the distance it moves a point `scale` from
99    /// its axis (see [`Pose`]'s).
100    fn stay(&self, at: &Self, scale: Scalar) -> GeopResult<Rows> {
101        match (self, at) {
102            (StateValue::Number(v), StateValue::Number(at)) => v.stay(at, scale),
103            (StateValue::Pose(pose), StateValue::Pose(at)) => pose.stay(at, scale),
104            _ => Err(GeopError::new(format!(
105                "{self:?} cannot stay at {at:?}: they are values of different kinds"
106            ))),
107        }
108    }
109
110    /// The same enclosure: what tells a step that changed nothing.
111    fn same(&self, other: &Self) -> bool {
112        self == other
113    }
114
115    /// A pose by a pose, as poses are (see [`Pose`]'s); a number by
116    /// nothing.
117    fn carried(&self, from: &Self, to: &Self) -> Option<Self> {
118        match (self, from, to) {
119            (StateValue::Pose(pose), StateValue::Pose(from), StateValue::Pose(to)) => {
120                pose.carried(from, to).map(StateValue::Pose)
121            }
122            _ => None,
123        }
124    }
125}