rgmsh/model/mod.rs
1//! Inspection and manipulation of Gmsh geometry models.
2//!
3//! There are two CAD engines:
4//! 1. The built-in Gmsh geometry kernel.
5//! 2. The `OpenCASCADE` geometry kernel.
6//!
7//! Either kernel should suffice for most projects.
8//!
9//! `OpenCASCADE` is a widely-used CAD engine, so it's a good default choice. You can directly define larger shapes without making their smaller components first.
10//! You also get access to powerful Boolean geometry operations for making complex shapes.
11//!
12//! The [Gmsh manual](http://gmsh.info/doc/texinfo/gmsh.html#Geometry-module)
13//! has more information on the differences between the two kernels:
14//!
15//! > The built-in CAD kernel provides a simple CAD engine based on a bottom-up boundary representation approach:
16//! > you need to first define points, then curves, then surfaces and finally volumes.
17//!
18//! > The `OpenCASCADE` kernel allows one to build models in the same bottom-up manner, or by using a
19//! > constructive solid geometry approach where solids are defined first.
20//! > Boolean operations can then be performed to modify them.
21//!
22//! The only way to get a model is through a `Gmsh` context object.
23//! ```
24//! # use rgmsh::{Gmsh, GmshResult};
25//! # fn main() -> GmshResult<()> {
26//! let gmsh = Gmsh::initialize()?;
27//! let mut geom = gmsh.create_native_model("model")?;
28//! # Ok(())
29//! # }
30//! ```
31//!
32//! The model is only valid for the lifetime of `Gmsh`.
33//! ```compile_fail
34//! # use rgmsh::{Gmsh, GmshResult};
35//! # fn main() -> GmshResult<()> {
36//! let gmsh = Gmsh::initialize()?;
37//! let mut geom = gmsh.create_occ_model("model")?;
38//!
39//! // -- do some stuff with geom
40//!
41//! // drop the Gmsh context
42//! std::mem::drop(gmsh);
43//! // try to use the model afterwards
44//! geom.add_point(0., 0., 0.)?; // won't compile
45//! # Ok(())
46//! # }
47//! ```
48//!
49//! ## Create, modify and delete shapes
50//! You can define points, lines, 2D surfaces and 3D volumes.
51//! After defining a shape, you'll get a geometry tag to identify[^unique] it.
52//! ```
53//! # use rgmsh::{Gmsh, GmshResult};
54//! # use rgmsh::model::{PointTag, CurveTag};
55//! # fn main() -> GmshResult<()> {
56//! # let gmsh = Gmsh::initialize()?;
57//! // make a model using the default geometry kernel and call it `model`.
58//! let mut geom = gmsh.create_native_model("model")?;
59//!
60//! // make a point
61//! let p1: PointTag = geom.add_point(0., 0., 0.)?;
62//! // and another
63//! let p2: PointTag = geom.add_point(1., 1., 0.)?;
64//!
65//! // create a line from the two points
66//! let l1: CurveTag = geom.add_line(p1, p2)?;
67//! # Ok(())
68//! # }
69//! ```
70//!
71//! There are two ways to make geometries in Gmsh: top-down and bottom-up.
72//!
73//! ### Top-down geometry with the `OpenCASCADE` kernel
74//! With the `OpenCASCADE` kernel, you can directly specify the shape you want to make.
75//! ```
76//! # use rgmsh::{Gmsh, GmshResult};
77//! # use rgmsh::model::{PointTag, CurveTag};
78//! # fn main() -> GmshResult<()> {
79//! # let gmsh = Gmsh::initialize()?;
80//! let mut geom = gmsh.create_occ_model("model")?;
81//!
82//! // make a box starting at (0, 0, 0) with side extents (1, 1, 1)
83//! let b = geom.add_box((0., 0., 0.), (1., 1., 1.))?;
84//!
85//! // make a sphere centered at (10, 10, 10) with radius 2.5
86//! let s = geom.add_sphere((10., 10., 10.), 2.5)?;
87//!
88//! // make a torus centered at (-1, -2, -3) with major radius 5 and minor radius 2
89//! let t = geom.add_torus((-1., -2., -3.), (5., 2.))?;
90//!
91//! # Ok(())
92//! # }
93//! ```
94//!
95//! ### Bottom-up geometries with either the `OpenCASCADE` or built-in kernel
96//!
97//! ## Geometry tags
98//! Geometry tags are used for:
99//! * accessing shape information,
100//! * making more complex shapes (like a line from two points),
101//! * removing a shape from the model
102//!
103//! The different geometry tags are:
104//! * `PointTag`
105//! * `CurveTag`
106//! * `WireTag`
107//! * `SurfaceTag`
108//! * `ShellTag`
109//! * `VolumeTag`
110//!
111//! Since tags can only be created from successful geometry operations, you can't
112//! use raw integers for tags.
113//! ```compile_fail
114//! # use rgmsh::{Gmsh, GmshResult};
115//! # use rgmsh::model::{PointTag, CurveTag};
116//! # fn main() -> GmshResult<()> {
117//! # let gmsh = Gmsh::initialize()?;
118//! # let geom = gmsh.create_native_model("model")?;
119//! // try to make a point from a raw integer
120//! let p1 = PointTag(1); // won't compile
121//! // try to make a line from two raw integers
122//! let l1 = CurveTag(1, 2); // won't compile
123//! # Ok(())
124//! # }
125//! ```
126//!
127//! This design differs from other Gmsh API
128//! implementations. For example, using the `C++` API, the following example will
129//! compile but cause a runtime error.
130//! ```cpp
131//! #include "gmsh.h"
132//! int main() {
133//! gmsh::initialize();
134//! gmsh::model::geo::addLine(1, 2); // (!)
135//! gmsh::finalize();
136//! }
137//! ```
138//! The Rust API avoids such bugs for a single model by only making tags available through API functions.
139//! However, the Rust API has a similar issue if there are two or more models.
140//! Since two models can have identical point tag values, tags from one can be used on the other.
141//!
142//! It's your responsibility to make sure tags are used with the right model.
143//!
144//! If you're lucky, using the wrong tags will cause a runtime error.
145//! ```
146//! # use rgmsh::{Gmsh, GmshResult};
147//! # use std::result::Result;
148//! # fn main() -> GmshResult<()> {
149//! # let gmsh = Gmsh::initialize()?;
150//! let mut geom_a = gmsh.create_occ_model("jimbo")?;
151//! let mut geom_b = gmsh.create_native_model("aircraft-carrier")?;
152//!
153//! let p_a = geom_a.add_point(0., 0., 0.)?;
154//!
155//! let p_b1 = geom_b.add_point(0., 1., 0.)?;
156//! let p_b2 = geom_b.add_point(1., 1., 0.)?;
157//!
158//! // points from different models can have the same value
159//! assert!(p_a == p_b1, "Point tags are different!");
160//!
161//! // Bad! Using tags from one model with another.
162//! let line = geom_a.add_line(p_b1, p_b2);
163//! assert!(line.is_err());
164//! # Ok(())
165//! # }
166//! ```
167//!
168//! If you're unlucky, the tags will exist in both models, causing a silent logic error in your program.
169//! In the API's eyes, you've given it valid tags, and it's going to go ahead and do what you asked for.
170//! ```
171//! # use rgmsh::{Gmsh, GmshResult};
172//! # use std::result::Result;
173//! # fn main() -> GmshResult<()> {
174//! # let gmsh = Gmsh::initialize()?;
175//! let mut geom_a = gmsh.create_occ_model("jimbo")?;
176//! let p_a1 = geom_a.add_point(0., 0., 0.)?;
177//! let p_a2 = geom_a.add_point(1., 0., 0.)?;
178//!
179//! let mut geom_b = gmsh.create_native_model("aircraft-carrier")?;
180//! let p_b1 = geom_b.add_point(0., 1., 1.)?;
181//! let p_b2 = geom_b.add_point(0., 1., 1.)?;
182//!
183//! // Very bad! A silent logic error. You're on your own debugging this one.
184//! let line = geom_a.add_line(p_b1, p_b2);
185//! assert!(line.is_ok());
186//! # Ok(())
187//! # }
188//! ```
189//!
190//! Nearly all geometry functions can fail. Fallible functions will result a `GmshResult`.
191//!
192//! You can use the `?` operator for terse error handling.
193//! ```
194//! # use rgmsh::{Gmsh, GmshResult};
195//! fn main() -> GmshResult<()> {
196//! let gmsh = Gmsh::initialize()?;
197//! let mut geom = gmsh.create_native_model("model")?;
198//!
199//! let p1 = geom.add_point(0., 0., 0.)?;
200//!
201//! Ok(())
202//! }
203//! ```
204//!
205//! ## Describing shapes using Physical Groups
206//! Physical Groups are Gmsh's way to associate information with geometries.
207//! Physical Groups only associate a name with geometry entities and it is up to client software
208//! to correctly interpret the Physical Group information.
209//!
210//! Some common uses for Physical Groups are:
211//! * Materials
212//! * Boundary conditions
213//! * Part names
214//!
215//! [^unique]: In most circumstances, tags are a unique identifier. There are some
216//! exceptions:
217//! * If tags are removed from a model, they can be used again for other shapes.
218//! * One Gmsh context can have many models. It's your responsibility to avoid
219//! using tags from one model in another.
220//!
221
222use crate::{check_main_error, check_model_error, get_cstring, Gmsh, GmshError, GmshResult};
223
224use std::ffi::{CStr, CString};
225use std::os::raw::c_int;
226
227use std::marker::PhantomData;
228use std::ops::Neg;
229
230// gmsh_sys interface
231pub use crate::interface::{geo::*, occ::*};
232
233pub mod shapes;
234pub use shapes::*;
235
236pub mod geo;
237pub mod occ;
238
239/// Add points to a geometry model inline.
240///
241/// You can use `add_points!` to create a series of points inline.
242///
243/// Both regular points and points with characteristic lengths are supported.
244///
245/// This macro returns a new `Vec<PointTag>`.
246/// ```
247/// # use rgmsh::{Gmsh, GmshResult, add_points};
248/// # fn main() -> GmshResult<()> {
249/// # let gmsh = Gmsh::initialize()?;
250/// let mut geom = gmsh.create_occ_model("model")?;
251/// let lc = 1e-2;
252/// let rect_pts = add_points![geom,
253/// (0., 0., 0.), // basic point
254/// (0.1, 0., 0., lc), // point with a target mesh size
255/// (0.1, 0.3, 0., lc),
256/// (0., 0.3, 0.)];
257///
258/// for pt in rect_pts.iter() {
259/// println!("{:?}", pt);
260/// }
261/// # Ok(())
262/// # }
263/// ```
264#[macro_export]
265macro_rules! add_points {
266 // base case
267 (@accum, $kernel_name:ident, $vec:ident) => {};
268 // point without a characteristic length
269 (@accum, $kernel_name:ident, $vec:ident, ($x:expr, $y:expr, $z:expr) $(, $tail:tt)*) => {
270 {
271 $vec.push($kernel_name.add_point($x, $y, $z)?);
272 add_points!(@accum, $kernel_name, $vec $(,$tail)*);
273 }
274 };
275 // point with a characteristic length
276 (@accum, $kernel_name:ident, $vec:ident, ($x:expr, $y:expr, $z:expr, $lc:expr) $(, $tail:tt)*) => {
277 {
278 $vec.push($kernel_name.add_point_with_lc($x, $y, $z, $lc)?);
279 add_points!(@accum, $kernel_name, $vec $(,$tail)*);
280 }
281 };
282 // match one more more points
283 ($kernel_name:ident, $($points:tt),+) => {
284 {
285 let mut temp_vec = Vec::new();
286 // use internal separator comma at the front
287 add_points!(@accum, $kernel_name, temp_vec $(,$points)*);
288 temp_vec
289 }
290 }
291}
292
293/// An instance of the built-in geometry kernel.
294pub struct GeoModel<'a> {
295 /// The model name.
296 pub name: &'static str,
297 /// The model name used to talk to C.
298 pub c_name: CString,
299 phantom: PhantomData<&'a Gmsh>,
300}
301
302/// An instance of the `OpenCASCADE` geometry kernel.
303pub struct OccModel<'a> {
304 /// The model name.
305 pub name: &'static str,
306 /// The model name used to talk to C.
307 pub c_name: CString,
308 phantom: PhantomData<&'a Gmsh>,
309}
310
311
312// General model methods
313macro_rules! impl_model {
314
315 (@kernel_prefix GeoModel, $fn_name: ident) => {
316 crate::interface::geo::$fn_name
317 };
318
319 (@kernel_prefix OccModel, $fn_name: ident) => {
320 crate::interface::occ::$fn_name
321 };
322
323 ($model_type: ident) => {
324 impl<'a> $model_type<'a> {
325 /// Create a new Gmsh model.
326 // todo: fix me for setting which model is the current one.
327 // idea: keep a list of already used model names and only allow one at once
328 #[must_use]
329 pub fn create(_: &'a Gmsh, name: &'static str) -> GmshResult<Self> {
330 let c_name = get_cstring(name)?;
331 unsafe {
332 let mut ierr: c_int = 0;
333 // also sets the added model as the current model
334 gmsh_sys::gmshModelAdd(c_name.as_ptr(), &mut ierr);
335 let model = $model_type {
336 name,
337 c_name,
338 phantom: PhantomData,
339 };
340 check_main_error!(ierr, model)
341 }
342 }
343
344 /// Remove model from Gmsh.
345 pub fn remove(self) -> GmshResult<()> {
346 // first set this model to the current model.
347 self.set_current()?;
348 // now, remove the current model
349 unsafe {
350 let mut ierr: c_int = 0;
351 gmsh_sys::gmshModelRemove(&mut ierr);
352 check_main_error!(ierr, ())
353 }
354 }
355
356 /// Set model to current model.
357 pub fn set_current(&self) -> GmshResult<()> {
358 unsafe {
359 let mut ierr: c_int = 0;
360 gmsh_sys::gmshModelSetCurrent(self.c_name.as_ptr(), &mut ierr);
361 match ierr {
362 0 => Ok(()),
363 _ => Err(GmshError::Execution),
364 }
365 }
366 }
367
368 /// Synchronize the underlying CAD representation.
369 pub fn synchronize(&mut self) -> GmshResult<()> {
370 self.set_current()?;
371 unsafe {
372 let mut ierr: c_int = 0;
373 let sync_fn = impl_model!(@kernel_prefix $model_type, synchronize);
374 sync_fn(&mut ierr);
375 check_model_error!(ierr, ())
376 }
377 }
378
379 /// Mesh the model.
380 // probably should move this to a dedicated model class
381 // with an inner Option(Mesh) and Option(Geo)
382 pub fn generate_mesh(&mut self, dim: i32) -> GmshResult<()> {
383 self.set_current()?;
384 // TODO think about synchronize by default?
385 self.synchronize()?;
386 unsafe {
387 let mut ierr: c_int = 0;
388 gmsh_sys::gmshModelMeshGenerate(dim, &mut ierr);
389 check_model_error!(ierr, ())
390 }
391 }
392 }
393 }
394}
395
396impl_model!(GeoModel);
397impl_model!(OccModel);
398
399// #[doc(hidden)]
400// #[must_use]
401// fn add_point_gen(
402// &mut self,
403// coords: (f64, f64, f64),
404// mesh_size: Option<f64>,
405// ) -> GmshResult<PointTag>;
406//
407// /// Add a point to the model by specifying its coordinates.
408// #[must_use]
409// fn add_point(&mut self, x: f64, y: f64, z: f64) -> GmshResult<PointTag> {
410// println!("added basic point");
411// self.add_point_gen((x, y, z), None)
412// }
413//
414// /// Add a point to the model and specify a target mesh size `lc` there.
415// #[must_use]
416// fn add_point_with_lc(&mut self, x: f64, y: f64, z: f64, lc: f64) -> GmshResult<PointTag> {
417// println!("added point with lc");
418// self.add_point_gen((x, y, z), Some(lc))
419// }
420//
421// /// Remove a point from the model.
422// fn remove_point(&mut self, p: PointTag) -> GmshResult<()>;
423//
424// /// Add a straight line between two points.
425// fn add_line(&mut self, p1: PointTag, p2: PointTag) -> GmshResult<CurveTag>;
426//
427// /// Add a curve loop from a closed set of curves.
428// fn add_curve_loop(&mut self, curves: &[CurveTag]) -> GmshResult<WireTag>;
429//
430// /// Add a surface from a WireTag of a closed curve set.
431// fn add_plane_surface(&mut self, closed_curve: WireTag) -> GmshResult<SurfaceTag>;
432//
433// /// Add a surface with holes from a WireTag of a boundary and a Wiretags of the holes.
434// fn add_plane_surface_with_holes(&mut self, boundary: WireTag, holes: &[WireTag]) -> GmshResult<SurfaceTag>;
435//
436// #[doc(hidden)]
437// fn add_plane_surface_gen(&mut self, curves: &[WireTag]) -> GmshResult<SurfaceTag>;
438//
439// #[doc(hidden)]
440// fn curve_or_surface_op<T: Into<CurveOrSurface>>(&mut self, gen_entity: T);
441//
442// /// Mesh the model.
443// // probably should move this to a dedicated model class
444// // with an inner Option(Mesh) and Option(Geo)
445// fn generate_mesh(&mut self, dim: i32) -> GmshResult<()> {
446// self.set_current()?;
447// // synchronize by default?
448// self.synchronize()?;
449// unsafe {
450// let mut ierr: c_int = 0;
451// gmsh_sys::gmshModelMeshGenerate(dim, &mut ierr);
452// check_model_error!(ierr, ())
453// }
454// }
455//}
456
457// // Implement kernel functions that follow a naming pattern.
458// #[doc(hidden)]
459// #[macro_export]
460// macro_rules! impl_kernel {
461//
462// // internal macro rules for prefixing similar geometry kernel functions
463// // Idea adapted from the rust-blas package here:
464// // https://github.com/mikkyang/rust-blas/pull/12
465// (@kernel_prefix Geo, $fn_name: ident) => {
466// crate::interface::geo::$fn_name
467// };
468//
469// (@kernel_prefix Occ, $fn_name: ident) => {
470// crate::interface::occ::$fn_name
471// };
472//
473// ($kernel_name: ident) => {
474// impl<'a> GeoKernel for $kernel_name<'a> {
475// //-----------------------------------------------------------------
476// // General kernel methods for all kernels
477// //-----------------------------------------------------------------
478//
479// fn name(&self) -> &'static str {
480// self.name
481// }
482//
483// fn c_name(&self) -> &CStr {
484// &self.c_name
485// }
486//
487// fn remove(self) -> GmshResult<()> {
488// // first set this model to the current model.
489// self.set_current()?;
490// // now, remove the current model
491// unsafe {
492// let mut ierr: c_int = 0;
493// gmsh_sys::gmshModelRemove(&mut ierr);
494// check_main_error!(ierr, ())
495// }
496// }
497//
498// //-----------------------------------------------------------------
499// // Prefix methods with a naming pattern for each kernel
500// //-----------------------------------------------------------------
501//
502// /// Synchronize the geometry model.
503// fn synchronize(&mut self) -> GmshResult<()> {
504// self.set_current()?;
505// unsafe {
506// let mut ierr: c_int = 0;
507// let sync_fn = impl_kernel!(@kernel_prefix $kernel_name, synchronize);
508// sync_fn(&mut ierr);
509// check_model_error!(ierr, ())
510// }
511// }
512//
513// #[doc(hidden)]
514// #[must_use]
515// fn add_point_gen(
516// &mut self,
517// coords: (f64, f64, f64),
518// mesh_size: Option<f64>,
519// ) -> GmshResult<PointTag> {
520// self.set_current()?;
521//
522// let (x, y, z) = coords;
523//
524// let lc = mesh_size.unwrap_or(0.);
525// let auto_number = -1;
526//
527// unsafe {
528// let mut ierr: c_int = 0;
529// let add_point_fn = impl_kernel!(@kernel_prefix $kernel_name, add_point);
530// let out_tag = add_point_fn(x, y, z, lc, auto_number, &mut ierr);
531// check_model_error!(ierr, PointTag(out_tag))
532// }
533// }
534//
535// /// Delete a point from the Gmsh model.
536// // todo: Genericize this for all GeometryTags
537// fn remove_point(&mut self, p: PointTag) -> GmshResult<()> {
538// self.set_current()?;
539// let raw_tag = p.0;
540// unsafe {
541// let vec_len = 1;
542// let is_recursive = 0;
543// let mut ierr: c_int = 0;
544// let remove_point_fn = impl_kernel!(@kernel_prefix $kernel_name, remove_point);
545// remove_point_fn([raw_tag].as_mut_ptr(), vec_len, is_recursive, &mut ierr);
546// check_model_error!(ierr, ())
547// }
548// }
549//
550// /// Add a straight line between two points.
551// #[must_use]
552// fn add_line(&mut self, p1: PointTag, p2: PointTag) -> GmshResult<CurveTag> {
553// self.set_current()?;
554// let auto_number = -1;
555// unsafe {
556// let mut ierr: c_int = 0;
557// let add_line_fn = impl_kernel!(@kernel_prefix $kernel_name, add_line);
558// let out_tag = add_line_fn(p1.to_raw(), p2.to_raw(), auto_number, &mut ierr);
559// check_model_error!(ierr, CurveTag(out_tag))
560// }
561// }
562//
563// /// Add a curve loop from a closed set of curves.
564// #[must_use]
565// fn add_curve_loop(&mut self, curves: &[CurveTag]) -> GmshResult<WireTag> {
566// self.set_current()?;
567// let mut raw_tags: Vec<_> = curves.iter().map(|c| c.to_raw()).collect();
568// let auto_number = -1;
569// unsafe {
570// let mut ierr: c_int = 0;
571// let add_curve_loop_fn = impl_kernel!(@kernel_prefix $kernel_name, add_curve_loop);
572// let out_tag = add_curve_loop_fn(raw_tags.as_mut_ptr(), raw_tags.len() as usize, auto_number, &mut ierr);
573// check_model_error!(ierr, WireTag(out_tag))
574// }
575// }
576//
577// /// Add a surface from a WireTag of a closed curve set.
578// #[must_use]
579// fn add_plane_surface(&mut self, boundary: WireTag) -> GmshResult<SurfaceTag> {
580// self.add_plane_surface_gen(&[boundary])
581// }
582//
583// /// Add a surface with holes.
584// #[must_use]
585// fn add_plane_surface_with_holes(&mut self, boundary: WireTag, holes: &[WireTag]) -> GmshResult<SurfaceTag> {
586// self.add_plane_surface_gen(&[&[boundary], holes].concat())
587// }
588//
589// #[doc(hidden)]
590// fn add_plane_surface_gen(&mut self, curves: &[WireTag]) -> GmshResult<SurfaceTag> {
591// self.set_current()?;
592// let mut raw_tags: Vec<_> = curves.iter().map(|c| c.to_raw()).collect();
593// let auto_number = -1;
594// unsafe {
595// let mut ierr: c_int = 0;
596// let add_plane_fn = impl_kernel!(@kernel_prefix $kernel_name, add_plane_surface);
597// let out_tag = add_plane_fn(raw_tags.as_mut_ptr(), raw_tags.len() as usize, auto_number, &mut ierr);
598// check_model_error!(ierr, SurfaceTag(out_tag))
599// }
600// }
601//
602// // idea for a certain operation that only works for curves and surfaces
603// fn curve_or_surface_op<T: Into<CurveOrSurface>>(&mut self, gen_entity: T) {
604// let entity = gen_entity.into();
605// match entity {
606// CurveOrSurface::Curve(CurveTag(ct)) => println!("Curve with tag {:?}", ct),
607// CurveOrSurface::Surface(SurfaceTag(ct)) => {
608// println!("Surface with tag {:?}", ct)
609// }
610// }
611// }
612// }
613// };
614// }
615
616#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
617/// A point tag. Points are used to build larger shapes. 0D.
618pub struct PointTag(i32);
619#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
620/// A curve tag, built from points. The curve type includes straight lines. 1D.
621pub struct CurveTag(i32);
622
623/// Curves have a direction from start to end.
624impl Neg for CurveTag {
625 type Output = Self;
626 /// Reverse the curve's direction.
627 fn neg(self) -> Self {
628 match self {
629 CurveTag(i) => Self(-i),
630 }
631 }
632}
633
634#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
635/// A wire tag. Wires are built from curves. Wires are a path of multiple curves. 1.5D.
636pub struct WireTag(i32);
637#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
638/// A surface tag. Surfaces are built from closed wires. 2D.
639pub struct SurfaceTag(i32);
640#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
641/// A shell tag. Shells are built from surface loops. 2.5D.
642pub struct ShellTag(i32);
643#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
644/// A volume tag. Volumes are built from closed shells. 3D.
645pub struct VolumeTag(i32);
646
647/// A trait for the different tags used by Gmsh.
648trait GmshTag {
649 /// The raw tag integer passed to the Gmsh library.
650 fn to_raw(&self) -> i32;
651}
652
653impl GmshTag for PointTag {
654 fn to_raw(&self) -> i32 {
655 self.0
656 }
657}
658
659impl GmshTag for CurveTag {
660 fn to_raw(&self) -> i32 {
661 self.0
662 }
663}
664
665impl GmshTag for WireTag {
666 fn to_raw(&self) -> i32 {
667 self.0
668 }
669}
670
671impl GmshTag for SurfaceTag {
672 fn to_raw(&self) -> i32 {
673 self.0
674 }
675}
676
677impl From<PointTag> for BasicShape {
678 fn from(t: PointTag) -> BasicShape {
679 BasicShape::Point(t)
680 }
681}
682
683impl From<CurveTag> for BasicShape {
684 fn from(t: CurveTag) -> BasicShape {
685 BasicShape::Curve(t)
686 }
687}
688
689/// Private module for sets of geometries passed and returned from functions.
690///
691/// Gmsh operations can be on multiple known types. We use enums for a compile-time
692/// check that the type is OK to use with that function.
693mod geometry_groups {
694 use super::*;
695
696 #[derive(Debug, Copy, Clone)]
697 /// The basic geometry types (points, curves, surfaces, and volumes).
698 pub enum BasicShape {
699 Point(PointTag),
700 Curve(CurveTag),
701 Surface(SurfaceTag),
702 Volume(VolumeTag),
703 }
704
705 #[derive(Debug, Copy, Clone)]
706 /// The full set of geometry types (`BasicGeometries` + wires + shells).
707 pub enum GeneralShape {
708 Point(PointTag),
709 Curve(CurveTag),
710 Wire(WireTag),
711 Surface(SurfaceTag),
712 Shell(ShellTag),
713 Volume(VolumeTag),
714 }
715
716 #[derive(Debug, Copy, Clone)]
717 /// Only curves or surfaces.
718 pub enum CurveOrSurface {
719 Curve(CurveTag),
720 Surface(SurfaceTag),
721 }
722}
723
724use geometry_groups::BasicShape;
725use geometry_groups::CurveOrSurface;
726
727type c_or_s = CurveOrSurface;
728
729impl From<CurveTag> for c_or_s {
730 fn from(t: CurveTag) -> c_or_s {
731 CurveOrSurface::Curve(t)
732 }
733}
734
735impl From<SurfaceTag> for CurveOrSurface {
736 fn from(t: SurfaceTag) -> CurveOrSurface {
737 CurveOrSurface::Surface(t)
738 }
739}
740
741/// Associated geometry information.
742#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
743struct PhysicalGroupTag(i32);