geop_core_part/datum.rs
1//! A [`Part`]'s datums: reference geometry — points, axes and planes the
2//! part is built *with*, not *of*. A sketch is placed on a datum plane, a
3//! datum axis is picked as a direction, a datum is built from another one.
4//! Named like any other entity.
5
6use geop_core_math::{
7 geop_error::{GeopError, GeopResult},
8 primitives::CoordinateSystem,
9 scalars::Scalar,
10};
11use serde::{Deserialize, Serialize};
12
13use crate::ids::DatumId;
14use crate::part::Part;
15
16/// What a datum stands for.
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum DatumKind {
20 /// The frame's origin — with the frame itself, a coordinate system.
21 Point,
22 /// The line through the frame's origin along its `w`.
23 Axis,
24 /// The plane through the frame's origin normal to its `w`; `u`/`v` are
25 /// a sketch's `x`/`y` on it.
26 Plane,
27}
28
29/// One datum: a right-handed orthonormal frame, and which part of it the
30/// datum stands for. Every datum has a whole frame, whatever its kind, so
31/// anything built on it — a sketch on a plane, a datum offset from a point
32/// — has axes to be built along.
33#[derive(Clone, Debug)]
34pub struct Datum<S: Scalar> {
35 pub kind: DatumKind,
36 pub frame: CoordinateSystem<S>,
37}
38
39impl<S: Scalar> Part<S> {
40 /// Adds `datum` to the part under `name`. Fails, leaving the part
41 /// unchanged, if `name` is already taken.
42 pub fn add_datum(&mut self, datum: Datum<S>, name: impl Into<String>) -> GeopResult<DatumId> {
43 let id = DatumId(self.fresh_id());
44 self.names.insert(id, name)?;
45 self.datums.insert(id, datum);
46 Ok(id)
47 }
48
49 pub fn datum(&self, id: DatumId) -> GeopResult<&Datum<S>> {
50 self.datums
51 .get(&id)
52 .ok_or_else(|| GeopError::new(format!("Part has no datum {id}")))
53 }
54
55 /// Every datum, in the order they were added.
56 pub fn datums(&self) -> impl Iterator<Item = (DatumId, &Datum<S>)> {
57 self.datums.iter().map(|(&id, d)| (id, d))
58 }
59}