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);