1pub use crate::{
24 joints::{Connector, CouplingKind, Joint, JointKind, Limits, MOTIONS, Motion, Transmission},
25 residuals::{Geometry, Kind},
26};
27pub use geop_ops_model::mate_entity::MateEntity;
28
29use geop_core_math::geop_error::GeopResult;
30use geop_core_program::{
31 element::{HasElements, Kind as ElementKind, whole},
32 target::{Target, TargetReference},
33};
34use geop_ops_model::selection::Role;
35use serde::{Deserialize, Serialize};
36
37fn describe_roles(roles: &[Role]) -> String {
39 match roles {
40 [] => String::new(),
41 [only] => only.describe().to_string(),
42 [rest @ .., last] => {
43 let rest: Vec<&str> = rest.iter().map(|r| r.describe()).collect();
44 format!("{} or {}", rest.join(", "), last.describe())
45 }
46 }
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum MateKind {
57 Constraint(Kind),
58 Joint(JointKind),
59 Coupling(CouplingKind),
60 Anchor(Anchor),
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
66#[serde(tag = "type", rename_all = "snake_case")]
67pub enum Anchor {
68 Fixed,
69}
70
71impl MateKind {
72 pub fn label(&self) -> &'static str {
73 match self {
74 MateKind::Constraint(kind) => kind.label(),
75 MateKind::Joint(kind) => kind.label(),
76 MateKind::Coupling(kind) => kind.transmission.label(),
77 MateKind::Anchor(Anchor::Fixed) => "Fixed",
78 }
79 }
80
81 pub fn roles(&self) -> &'static [Role] {
86 match self {
87 MateKind::Constraint(kind) => kind.roles(),
88 MateKind::Joint(_) | MateKind::Coupling(_) | MateKind::Anchor(_) => &[],
89 }
90 }
91}
92
93pub fn joint_parameter(mate: &str, motion: Motion) -> String {
97 format!("{mate}.{}", motion.name())
98}
99
100pub fn foot_parameter(mate: &str, foot: &str) -> String {
103 format!("{mate}.{foot}")
104}
105
106#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
113pub struct Mate {
114 #[serde(flatten)]
115 pub kind: MateKind,
116 #[serde(default)]
117 pub entities: Vec<MateEntity>,
118 #[serde(default, skip_serializing_if = "Vec::is_empty")]
121 pub joints: Vec<String>,
122}
123
124impl Mate {
125 pub fn constraint(kind: Kind, entities: Vec<MateEntity>) -> Self {
127 Mate {
128 kind: MateKind::Constraint(kind),
129 entities,
130 joints: Vec::new(),
131 }
132 }
133
134 pub fn joint(kind: JointKind, entities: Vec<MateEntity>) -> Self {
137 Mate {
138 kind: MateKind::Joint(kind),
139 entities,
140 joints: Vec::new(),
141 }
142 }
143
144 pub fn fixed_here() -> Self {
147 Mate {
148 kind: MateKind::Anchor(Anchor::Fixed),
149 entities: Vec::new(),
150 joints: Vec::new(),
151 }
152 }
153
154 pub fn is_fixed(&self) -> bool {
156 matches!(self.kind, MateKind::Anchor(Anchor::Fixed))
157 }
158
159 pub fn coupling(kind: CouplingKind, joints: Vec<String>) -> Self {
161 Mate {
162 kind: MateKind::Coupling(kind),
163 entities: Vec::new(),
164 joints,
165 }
166 }
167
168 pub fn pair(&self) -> Option<[&MateEntity; 2]> {
170 match self.entities.as_slice() {
171 [a, b] => Some([a, b]),
172 _ => None,
173 }
174 }
175
176 pub fn is_complete(&self) -> bool {
179 match self.kind {
180 MateKind::Coupling(_) => self.joints.len() == 2,
181 MateKind::Anchor(_) => self.entities.len() == 1,
182 _ => self.pair().is_some(),
183 }
184 }
185
186 pub fn needs(&self) -> String {
189 match self.kind {
190 MateKind::Constraint(kind) => match (kind.needs(0), kind.needs(1)) {
191 (Some(first), Some(second)) => format!("first {first}, then {second}"),
192 _ => describe_roles(self.kind.roles()),
193 },
194 MateKind::Joint(_) => {
195 "a face, an edge, a point or a datum: where it is picked, a frame is put"
196 .to_string()
197 }
198 MateKind::Anchor(_) => "an entity of the part to hold".to_string(),
199 MateKind::Coupling(kind) => match kind.transmission.motions() {
200 [Motion::Turn, Motion::Turn] => "two joints that turn".to_string(),
201 [a, b] => format!("a joint that {}s, then one that {}s", a.verb(), b.verb()),
202 },
203 }
204 }
205}
206
207#[derive(Clone, Debug, PartialEq)]
212pub struct On<T> {
213 pub body: Option<String>,
214 pub what: T,
215 pub entity: MateEntity,
216}
217
218#[derive(Clone, Debug, PartialEq)]
222pub enum Resolved {
223 Constraint {
224 kind: Kind,
225 a: On<Geometry>,
226 b: On<Geometry>,
227 },
228 Joint {
229 kind: JointKind,
230 a: On<Connector>,
231 b: On<Connector>,
232 },
233 Coupling {
235 kind: CouplingKind,
236 joints: [String; 2],
237 },
238 Fixed { body: String },
240}
241
242impl Resolved {
243 pub fn entities(&self) -> Vec<MateEntity> {
246 match self {
247 Resolved::Constraint { a, b, .. } => vec![a.entity.clone(), b.entity.clone()],
248 Resolved::Joint { a, b, .. } => vec![a.entity.clone(), b.entity.clone()],
249 Resolved::Coupling { .. } | Resolved::Fixed { .. } => Vec::new(),
250 }
251 }
252}
253
254const PREFIX: &str = "mate:";
256
257pub fn mate(name: &str) -> TargetReference<Resolved> {
259 TargetReference::new(format!("{PREFIX}{name}"))
260}
261
262pub fn mate_name(target: &str) -> Option<&str> {
264 target.strip_prefix(PREFIX)
265}
266
267impl Target for Resolved {}
269
270impl ElementKind for Resolved {
272 const NAME: &'static str = "mate";
273}
274
275impl HasElements<Resolved> for Resolved {
276 type Element<'a> = &'a Resolved;
277
278 fn element(&self, name: &str) -> GeopResult<&Resolved> {
279 whole(self, name)
280 }
281
282 fn names_target(name: &str) -> bool {
283 name.starts_with(PREFIX)
284 }
285}