omf/
grid.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    Array, Vector3,
6    array::Constraint,
7    array_type,
8    validate::{Validate, Validator},
9};
10
11/// Defines a 2D grid spacing and size.
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
13#[serde(tag = "type")]
14pub enum Grid2 {
15    /// Regularly spaced cells.
16    ///
17    /// <!--grid2_regular.svg-->
18    #[doc = include_str!("../docs/images/grid2_regular.svg")]
19    Regular {
20        /// The cell size in the U and V axes. Both must be greater than zero.
21        size: [f64; 2],
22        /// The number of cells in the U and V axes. Both must be greater than zero.
23        count: [u32; 2],
24    },
25    /// Tensor cells, where each row and column can have a different size.
26    ///
27    /// <!--grid2_tensor.svg-->
28    #[doc = include_str!("../docs/images/grid2_tensor.svg")]
29    Tensor {
30        /// Array with `Scalar` type storing the size of each cell along the U axis.
31        /// These sizes must be greater than zero.
32        u: Array<array_type::Scalar>,
33        /// Array with `Scalar` type storing the size of each cell along the V axis.
34        /// These sizes must be greater than zero.
35        v: Array<array_type::Scalar>,
36    },
37}
38
39impl Grid2 {
40    /// Create a 2D regular grid from the cell size and count.
41    pub fn from_size_and_count(size: [f64; 2], count: [u32; 2]) -> Self {
42        Self::Regular { size, count }
43    }
44
45    /// Create a 2D tensor grid from the size arrays.
46    pub fn from_arrays(u: Array<array_type::Scalar>, v: Array<array_type::Scalar>) -> Self {
47        Self::Tensor { u, v }
48    }
49
50    /// Returns the number of cells in each axis.
51    pub fn count(&self) -> [u32; 2] {
52        match self {
53            Self::Regular { count, .. } => *count,
54            Self::Tensor { u, v } => [u.item_count() as u32, v.item_count() as u32],
55        }
56    }
57
58    /// Returns the total number of cells.
59    pub fn flat_count(&self) -> u64 {
60        self.count().into_iter().map(u64::from).product()
61    }
62
63    /// Returns the total number of cell corners.
64    pub fn flat_corner_count(&self) -> u64 {
65        self.count().into_iter().map(|n| u64::from(n) + 1).product()
66    }
67}
68
69impl Default for Grid2 {
70    /// Creates a regular grid with size `[1.0, 1.0]` and count `[1, 1]`.
71    fn default() -> Self {
72        Self::Regular {
73            size: [1.0, 1.0],
74            count: [1, 1],
75        }
76    }
77}
78
79/// Defines a 3D grid spacing and size.
80#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
81#[serde(tag = "type")]
82#[allow(clippy::large_enum_variant)]
83pub enum Grid3 {
84    /// Regularly spaced cells.
85    ///
86    /// <!--grid3_regular.svg-->
87    #[doc = include_str!("../docs/images/grid3_regular.svg")]
88    Regular {
89        /// The block size in the U and V axes. All must be greater than zero.
90        size: Vector3,
91        /// The number of cells in the U, V, and W axes. All must be greater than zero.
92        count: [u32; 3],
93    },
94    /// Tensor cells, where each row, column, and layer can have a different size. All sizes
95    /// must be greater than zero.
96    ///
97    /// <!--grid3_tensor.svg-->
98    #[doc = include_str!("../docs/images/grid3_tensor.svg")]
99    Tensor {
100        /// Array with `Scalar` type storing the size of each cell along the U axis.
101        /// These sizes must be greater than zero.
102        u: Array<array_type::Scalar>,
103        /// Array with `Scalar` type storing the size of each cell along the V axis.
104        /// These sizes must be greater than zero.
105        v: Array<array_type::Scalar>,
106        /// Array with `Scalar` type storing the size of each cell along the W axis.
107        /// These sizes must be greater than zero.
108        w: Array<array_type::Scalar>,
109    },
110}
111
112impl Grid3 {
113    /// Create a 3D regular grid from the block size and count.
114    pub fn from_size_and_count(size: Vector3, count: [u32; 3]) -> Self {
115        Self::Regular { size, count }
116    }
117
118    /// Create a 3D tensor grid from the size arrays.
119    pub fn from_arrays(
120        u: Array<array_type::Scalar>,
121        v: Array<array_type::Scalar>,
122        w: Array<array_type::Scalar>,
123    ) -> Self {
124        Self::Tensor { u, v, w }
125    }
126
127    /// Returns the number of blocks in each axis.
128    pub fn count(&self) -> [u32; 3] {
129        match self {
130            Self::Regular { count, .. } => *count,
131            // validation checks that this cast is valid
132            Self::Tensor { u, v, w } => [
133                u.item_count() as u32,
134                v.item_count() as u32,
135                w.item_count() as u32,
136            ],
137        }
138    }
139
140    /// Returns the total number of blocks.
141    pub fn flat_count(&self) -> u64 {
142        self.count().iter().map(|n| u64::from(*n)).product()
143    }
144
145    /// Returns the total number of block corners.
146    pub fn flat_corner_count(&self) -> u64 {
147        self.count().iter().map(|n| u64::from(*n) + 1).product()
148    }
149}
150
151impl Default for Grid3 {
152    fn default() -> Self {
153        Self::Regular {
154            size: [1.0, 1.0, 1.0],
155            count: [1, 1, 1],
156        }
157    }
158}
159
160const fn i() -> Vector3 {
161    [1.0, 0.0, 0.0]
162}
163
164const fn j() -> Vector3 {
165    [0.0, 1.0, 0.0]
166}
167
168const fn k() -> Vector3 {
169    [0.0, 0.0, 1.0]
170}
171
172/// Defines the position and orientation of a 2D plane in 3D space.
173#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
174#[repr(C)]
175pub struct Orient2 {
176    /// Origin point relative to the project origin and coordinate reference.
177    pub origin: Vector3,
178    /// The direction of the U axis of the plane. Must be a unit vector. Default [1, 0, 0].
179    ///
180    /// Must also be perpendicular to the 'v' in grid surfaces.
181    #[serde(default = "i")]
182    pub u: Vector3,
183    /// The direction of the V axis of the plane. Must be a unit vector. Default [0, 1, 0].
184    ///
185    /// Must also be perpendicular to the 'u' in grid surfaces.
186    #[serde(default = "j")]
187    pub v: Vector3,
188}
189
190impl Orient2 {
191    /// Creates a new 2D orientation.
192    pub fn new(origin: Vector3, u: Vector3, v: Vector3) -> Self {
193        Self { origin, u, v }
194    }
195
196    /// Creates a new axis-aligned 2D orientation.
197    pub fn from_origin(origin: Vector3) -> Self {
198        Self::new(origin, i(), j())
199    }
200
201    pub(crate) fn validate_ortho(&self, val: &mut Validator) {
202        val.enter("Orient2").vectors_ortho2(self.u, self.v);
203    }
204}
205
206impl Default for Orient2 {
207    /// Creates a new axis-aligned 2D orientation at the origin.
208    fn default() -> Self {
209        Self {
210            origin: [0.0; 3],
211            u: i(),
212            v: j(),
213        }
214    }
215}
216
217/// Defines the position and orientation of a 3D sub-space.
218#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
219#[repr(C)]
220pub struct Orient3 {
221    /// Origin point relative to the project origin and coordinate reference.
222    pub origin: Vector3,
223    /// The direction of the U axis of the grid. Must be a unit vector perpendicular to
224    /// `v` and 'w'. Default [1, 0, 0].
225    #[serde(default = "i")]
226    pub u: Vector3,
227    /// The direction of the V axis of the grid. Must be a unit vector perpendicular to
228    /// `u` and 'w'. Default [0, 1, 0].
229    #[serde(default = "j")]
230    pub v: Vector3,
231    /// The direction of the W axis of the grid. Must be a unit vector perpendicular to
232    /// `u` and 'v'. Default [0, 0, 1].
233    #[serde(default = "k")]
234    pub w: Vector3,
235}
236
237impl Orient3 {
238    /// Creates a new 3D orientation.
239    pub fn new(origin: Vector3, u: Vector3, v: Vector3, w: Vector3) -> Self {
240        Self { origin, u, v, w }
241    }
242
243    /// Creates a new axis-aligned 3D orientation.
244    pub fn from_origin(origin: Vector3) -> Self {
245        Self::new(origin, i(), j(), k())
246    }
247}
248
249impl Default for Orient3 {
250    fn default() -> Self {
251        Self {
252            origin: [0.0; 3],
253            u: i(),
254            v: j(),
255            w: k(),
256        }
257    }
258}
259
260impl Validate for Grid2 {
261    fn validate_inner(&mut self, val: &mut Validator) {
262        match self {
263            Grid2::Regular { size, count } => {
264                val.enter("Grid2::Regular")
265                    .finite_seq(*size, "size")
266                    .above_zero_seq(*size, "size")
267                    .above_zero_seq(*count, "count");
268            }
269            Grid2::Tensor { u, v } => {
270                val.enter("Grid2::Tensor")
271                    .grid_count(&[u.item_count(), v.item_count()])
272                    .array(u, Constraint::Size, "u")
273                    .array(v, Constraint::Size, "v");
274            }
275        }
276    }
277}
278
279impl Validate for Grid3 {
280    fn validate_inner(&mut self, val: &mut Validator) {
281        match self {
282            Grid3::Regular { size, count } => {
283                val.enter("Grid3::Regular")
284                    .finite_seq(*size, "size")
285                    .above_zero_seq(*size, "size")
286                    .above_zero_seq(*count, "count");
287            }
288            Grid3::Tensor { u, v, w } => {
289                val.enter("Grid3::Tensor")
290                    .grid_count(&[u.item_count(), v.item_count(), w.item_count()])
291                    .array(u, Constraint::Size, "u")
292                    .array(v, Constraint::Size, "v")
293                    .array(w, Constraint::Size, "w");
294            }
295        }
296    }
297}
298
299impl Validate for Orient2 {
300    fn validate_inner(&mut self, val: &mut Validator) {
301        val.enter("Orient2")
302            .finite_seq(self.origin, "origin")
303            .unit_vector(self.u, "u")
304            .unit_vector(self.v, "v");
305    }
306}
307
308impl Validate for Orient3 {
309    fn validate_inner(&mut self, val: &mut Validator) {
310        val.enter("Orient3")
311            .finite_seq(self.origin, "origin")
312            .unit_vector(self.u, "u")
313            .unit_vector(self.v, "v")
314            .unit_vector(self.w, "w")
315            .vectors_ortho3(self.u, self.v, self.w);
316    }
317}