omf/
geometry.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    Array, BlockModel, Element, Grid2, Location, Orient2, Vector3,
6    array::Constraint,
7    array_type,
8    validate::{Validate, Validator},
9};
10
11pub(crate) fn zero_origin(v: &Vector3) -> bool {
12    *v == [0.0, 0.0, 0.0]
13}
14
15/// Selects the type of geometry in an [`Element`](crate::Element) from several options.
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
17#[serde(tag = "type")]
18pub enum Geometry {
19    PointSet(PointSet),
20    LineSet(LineSet),
21    Surface(Surface),
22    GridSurface(GridSurface),
23    BlockModel(BlockModel),
24    Composite(Composite),
25}
26
27impl Geometry {
28    /// Returns the valid locations for attributes on this geometry.
29    pub fn valid_locations(&self) -> &'static [Location] {
30        match self {
31            Self::PointSet(_) => &[Location::Vertices],
32            Self::LineSet(_) => &[Location::Vertices, Location::Primitives],
33            Self::Surface(_) => &[Location::Vertices, Location::Primitives],
34            Self::GridSurface(_) => &[Location::Vertices, Location::Primitives],
35            Self::Composite(_) => &[Location::Elements],
36            Self::BlockModel(b) if b.has_subblocks() => {
37                &[Location::Subblocks, Location::Primitives]
38            }
39            Self::BlockModel(_) => &[Location::Primitives],
40        }
41    }
42
43    /// Returns the length of the given location, if valid.
44    pub fn location_len(&self, location: Location) -> Option<u64> {
45        if location == Location::Projected {
46            Some(0)
47        } else {
48            match self {
49                Self::PointSet(p) => p.location_len(location),
50                Self::LineSet(l) => l.location_len(location),
51                Self::Surface(s) => s.location_len(location),
52                Self::GridSurface(t) => t.location_len(location),
53                Self::BlockModel(b) => b.location_len(location),
54                Self::Composite(c) => c.location_len(location),
55            }
56        }
57    }
58
59    pub(crate) fn type_name(&self) -> &'static str {
60        match self {
61            Self::PointSet(_) => "PointSet",
62            Self::LineSet(_) => "LineSet",
63            Self::Surface(_) => "Surface",
64            Self::GridSurface(_) => "GridSurface",
65            Self::Composite(_) => "Composite",
66            Self::BlockModel(b) if b.has_subblocks() => "BlockModel(sub-blocked)",
67            Self::BlockModel(_) => "BlockModel",
68        }
69    }
70}
71
72impl From<PointSet> for Geometry {
73    fn from(value: PointSet) -> Self {
74        Self::PointSet(value)
75    }
76}
77
78impl From<LineSet> for Geometry {
79    fn from(value: LineSet) -> Self {
80        Self::LineSet(value)
81    }
82}
83
84impl From<Surface> for Geometry {
85    fn from(value: Surface) -> Self {
86        Self::Surface(value)
87    }
88}
89
90impl From<GridSurface> for Geometry {
91    fn from(value: GridSurface) -> Self {
92        Self::GridSurface(value)
93    }
94}
95
96impl From<BlockModel> for Geometry {
97    fn from(value: BlockModel) -> Self {
98        Self::BlockModel(value)
99    }
100}
101
102impl From<Composite> for Geometry {
103    fn from(value: Composite) -> Self {
104        Self::Composite(value)
105    }
106}
107
108/// Point set geometry.
109///
110/// ### Attribute Locations
111///
112/// - [`Vertices`](crate::Location::Vertices) puts attribute values on the points.
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
114pub struct PointSet {
115    /// Origin of the points relative to the project coordinate reference system.
116    #[serde(default, skip_serializing_if = "zero_origin")]
117    pub origin: Vector3,
118    /// Array with `Vertex` type storing the vertex locations.
119    ///
120    /// Add `origin` and the [project](crate::Project) origin to get the locations relative
121    /// to the project coordinate reference system.
122    pub vertices: Array<array_type::Vertex>,
123}
124
125impl PointSet {
126    pub fn new(vertices: Array<array_type::Vertex>) -> Self {
127        Self::with_origin(vertices, Default::default())
128    }
129
130    pub fn with_origin(vertices: Array<array_type::Vertex>, origin: Vector3) -> Self {
131        Self { origin, vertices }
132    }
133
134    pub fn location_len(&self, location: Location) -> Option<u64> {
135        match location {
136            Location::Vertices => Some(self.vertices.item_count()),
137            _ => None,
138        }
139    }
140}
141
142/// A set of line segments.
143///
144/// ### Attribute Locations
145///
146/// - [`Vertices`](crate::Location::Vertices) puts attribute values on the vertices.
147///
148/// - [`Primitives`](crate::Location::Primitives) puts attribute values on the line segments.
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
150pub struct LineSet {
151    /// Origin of the lines relative to the project coordinate reference system.
152    #[serde(default, skip_serializing_if = "zero_origin")]
153    pub origin: Vector3,
154    /// Array with `Vertex` type storing the vertex locations.
155    ///
156    /// Add `origin` and the [project](crate::Project) origin to get the locations relative
157    /// to the project coordinate reference system.
158    pub vertices: Array<array_type::Vertex>,
159    /// Array with `Segment` type storing each segment as a pair of indices into `vertices`.
160    pub segments: Array<array_type::Segment>,
161}
162
163impl LineSet {
164    pub fn new(vertices: Array<array_type::Vertex>, segments: Array<array_type::Segment>) -> Self {
165        Self::with_origin(vertices, segments, Default::default())
166    }
167
168    pub fn with_origin(
169        vertices: Array<array_type::Vertex>,
170        segments: Array<array_type::Segment>,
171        origin: Vector3,
172    ) -> Self {
173        Self {
174            origin,
175            vertices,
176            segments,
177        }
178    }
179
180    pub fn location_len(&self, location: Location) -> Option<u64> {
181        match location {
182            Location::Vertices => Some(self.vertices.item_count()),
183            Location::Primitives => Some(self.segments.item_count()),
184            _ => None,
185        }
186    }
187}
188
189/// A surface made up of triangles.
190///
191/// ### Attribute Locations
192///
193/// - [`Vertices`](crate::Location::Vertices) puts attribute values on the vertices.
194///
195/// - [`Primitives`](crate::Location::Primitives) puts attribute values on the triangles.
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
197pub struct Surface {
198    /// Origin of the surface relative to the project coordinate reference system.
199    #[serde(default, skip_serializing_if = "zero_origin")]
200    pub origin: Vector3,
201    /// Array with `Vertex` type storing the vertex locations.
202    ///
203    /// Add `origin` and the [project](crate::Project) origin to get the locations relative
204    /// to the project coordinate reference system.
205    pub vertices: Array<array_type::Vertex>,
206    /// Array with `Triangle` type storing each triangle as a triple of indices into `vertices`.
207    /// Triangle winding should be counter-clockwise around an outward-pointing normal.
208    pub triangles: Array<array_type::Triangle>,
209}
210
211impl Surface {
212    pub fn new(
213        vertices: Array<array_type::Vertex>,
214        triangles: Array<array_type::Triangle>,
215    ) -> Self {
216        Self::with_origin(vertices, triangles, Default::default())
217    }
218
219    pub fn with_origin(
220        vertices: Array<array_type::Vertex>,
221        triangles: Array<array_type::Triangle>,
222        origin: Vector3,
223    ) -> Self {
224        Self {
225            origin,
226            vertices,
227            triangles,
228        }
229    }
230
231    pub fn location_len(&self, location: Location) -> Option<u64> {
232        match location {
233            Location::Vertices => Some(self.vertices.item_count()),
234            Location::Primitives => Some(self.triangles.item_count()),
235            _ => None,
236        }
237    }
238}
239
240/// A surface defined by a 2D grid a height on each grid vertex.
241///
242/// ### Attribute Locations
243///
244/// - [`Vertices`](crate::Location::Vertices) puts attribute values on the grid vertices.
245///
246/// - [`Primitives`](crate::Location::Primitives) puts attribute values on the grid cells.
247#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
248pub struct GridSurface {
249    /// Position and orientation of the surface.
250    pub orient: Orient2,
251    /// 2D grid definition, which can be regular or tensor.
252    pub grid: Grid2,
253    /// Array with `Scalar` type storing the offset of each grid vertex from the place.
254    /// Heights may be positive or negative. Will be absent from flat 2D grids.
255    pub heights: Option<Array<array_type::Scalar>>,
256}
257
258impl GridSurface {
259    pub fn new(orient: Orient2, grid: Grid2, heights: Option<Array<array_type::Scalar>>) -> Self {
260        Self {
261            orient,
262            grid,
263            heights,
264        }
265    }
266
267    pub fn location_len(&self, location: Location) -> Option<u64> {
268        match location {
269            Location::Vertices => Some(self.grid.flat_corner_count()),
270            Location::Primitives => Some(self.grid.flat_count()),
271            _ => None,
272        }
273    }
274}
275
276/// A container for sub-elements.
277///
278/// ### Attribute Locations
279///
280/// - [`Elements`](crate::Location::Elements) puts attribute values on elements.
281#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
282pub struct Composite {
283    #[serde(default)]
284    pub elements: Vec<Element>,
285}
286
287impl Composite {
288    pub fn new(elements: Vec<Element>) -> Self {
289        Self { elements }
290    }
291
292    pub fn location_len(&self, location: Location) -> Option<u64> {
293        match location {
294            Location::Elements => Some(self.elements.len().try_into().expect("usize fits in u64")),
295            _ => None,
296        }
297    }
298}
299
300impl Validate for Geometry {
301    fn validate_inner(&mut self, val: &mut Validator) {
302        let v = val.enter("Geometry");
303        match self {
304            Self::PointSet(x) => v.obj(x),
305            Self::LineSet(x) => v.obj(x),
306            Self::Surface(x) => v.obj(x),
307            Self::GridSurface(x) => v.obj(x),
308            Self::BlockModel(x) => v.obj(x),
309            Self::Composite(x) => v.obj(x),
310        };
311    }
312}
313
314impl Validate for PointSet {
315    fn validate_inner(&mut self, val: &mut Validator) {
316        val.enter("PointSet")
317            .finite_seq(self.origin, "origin")
318            .array(&mut self.vertices, Constraint::Vertex, "vertices");
319    }
320}
321
322impl Validate for LineSet {
323    fn validate_inner(&mut self, val: &mut Validator) {
324        val.enter("LineSet")
325            .finite_seq(self.origin, "origin")
326            .array(&mut self.vertices, Constraint::Vertex, "vertices")
327            .array(
328                &mut self.segments,
329                Constraint::Segment(self.vertices.item_count()),
330                "segments",
331            );
332    }
333}
334
335impl Validate for Surface {
336    fn validate_inner(&mut self, val: &mut Validator) {
337        val.enter("Surface")
338            .finite_seq(self.origin, "origin")
339            .array(&mut self.vertices, Constraint::Vertex, "vertices")
340            .array(
341                &mut self.triangles,
342                Constraint::Triangle(self.vertices.item_count()),
343                "triangles",
344            );
345    }
346}
347
348impl Validate for GridSurface {
349    fn validate_inner(&mut self, val: &mut Validator) {
350        let mut val = val
351            .enter("GridSurface")
352            .obj(&mut self.orient)
353            .obj(&mut self.grid)
354            .array_opt(self.heights.as_mut(), Constraint::Scalar, "heights")
355            .array_size_opt(
356                self.heights.as_ref().map(|h| h.item_count()),
357                self.grid.flat_corner_count(),
358                "heights",
359            );
360        self.orient.validate_ortho(&mut val);
361    }
362}
363
364impl Validate for Composite {
365    fn validate_inner(&mut self, val: &mut Validator) {
366        val.enter("Composite").objs(&mut self.elements).unique(
367            self.elements.iter().map(|e| &e.name),
368            "elements[..]::name",
369            false,
370        );
371    }
372}