Skip to main content

bempp_octree/
geometry.rs

1//! Geometry information
2
3use mpi::traits::Equivalence;
4
5use crate::constants::DEEPEST_LEVEL;
6
7/// Definition of a point in 3d space.
8///
9/// A point consists of three coordinates and a global id.
10/// The global id makes it easier to identify points as they
11/// are distributed across MPI nodes.
12#[derive(Clone, Copy, Equivalence)]
13pub struct Point {
14    coords: [f64; 3],
15    global_id: usize,
16}
17
18impl Point {
19    /// Create a new point from coordinates and global id.
20    pub fn new(coords: [f64; 3], global_id: usize) -> Self {
21        Self { coords, global_id }
22    }
23
24    /// Return the coordintes of a point.
25    pub fn coords(&self) -> [f64; 3] {
26        self.coords
27    }
28
29    /// Return a mutable pointer to the coordinates.
30    pub fn coords_mut(&mut self) -> &mut [f64; 3] {
31        &mut self.coords
32    }
33
34    /// Return the global id of the point.
35    pub fn global_id(&self) -> usize {
36        self.global_id
37    }
38}
39
40/// A bounding box describes geometry in which an Octree lives.
41pub struct PhysicalBox {
42    coords: [f64; 6],
43}
44
45impl PhysicalBox {
46    /// Create a new bounding box.
47    ///
48    /// The coordinates are given by `[xmin, ymin, zmin, xmax, ymax, zmax]`.
49    pub fn new(coords: [f64; 6]) -> Self {
50        Self { coords }
51    }
52
53    /// Give a slice of points. Compute an associated bounding box.
54    ///
55    /// The created bounding box is slightly bigger than the actual point set.
56    /// This is to make sure that no point lies exactly on the boundary of the box.
57    pub fn from_points(points: &[Point]) -> PhysicalBox {
58        let mut xmin = f64::MAX;
59        let mut xmax = f64::MIN;
60
61        let mut ymin = f64::MAX;
62        let mut ymax = f64::MIN;
63
64        let mut zmin = f64::MAX;
65        let mut zmax = f64::MIN;
66
67        for point in points {
68            let x = point.coords()[0];
69            let y = point.coords()[1];
70            let z = point.coords()[2];
71
72            xmin = f64::min(xmin, x);
73            xmax = f64::max(xmax, x);
74
75            ymin = f64::min(ymin, y);
76            ymax = f64::max(ymax, y);
77
78            zmin = f64::min(zmin, z);
79            zmax = f64::max(zmax, z);
80        }
81
82        // We want the bounding box to be slightly bigger
83        // than the actual point set to avoid issues
84        // at the edge of the bounding box.
85
86        let xdiam = xmax - xmin;
87        let ydiam = ymax - ymin;
88        let zdiam = zmax - zmin;
89
90        let xmean = xmin + 0.5 * xdiam;
91        let ymean = ymin + 0.5 * ydiam;
92        let zmean = zmin + 0.5 * zdiam;
93
94        // We increase diameters by box size on deepest level
95        // and use the maximum diameter to compute a
96        // cubic bounding box.
97
98        let deepest_box_diam = 1.0 / (1 << DEEPEST_LEVEL) as f64;
99
100        let max_diam = [xdiam, ydiam, zdiam].into_iter().reduce(f64::max).unwrap();
101
102        let max_diam = max_diam * (1.0 + deepest_box_diam);
103
104        PhysicalBox {
105            coords: [
106                xmean - 0.5 * max_diam,
107                ymean - 0.5 * max_diam,
108                zmean - 0.5 * max_diam,
109                xmean + 0.5 * max_diam,
110                ymean + 0.5 * max_diam,
111                zmean + 0.5 * max_diam,
112            ],
113        }
114    }
115
116    /// Return coordinates
117    pub fn coordinates(&self) -> [f64; 6] {
118        self.coords
119    }
120
121    /// Map a point from the reference box [0, 1]^3 to the bounding box.
122    pub fn reference_to_physical(&self, point: [f64; 3]) -> [f64; 3] {
123        let [xmin, ymin, zmin, xmax, ymax, zmax] = self.coords;
124
125        [
126            xmin + (xmax - xmin) * point[0],
127            ymin + (ymax - ymin) * point[1],
128            zmin + (zmax - zmin) * point[2],
129        ]
130    }
131
132    /// Map a point from the physical domain to the reference box.
133    pub fn physical_to_reference(&self, point: [f64; 3]) -> [f64; 3] {
134        let [xmin, ymin, zmin, xmax, ymax, zmax] = self.coords;
135
136        [
137            (point[0] - xmin) / (xmax - xmin),
138            (point[1] - ymin) / (ymax - ymin),
139            (point[2] - zmin) / (zmax - zmin),
140        ]
141    }
142
143    /// Return an ordered list of corners of the box.
144    ///
145    /// The ordering of the corners on the unit cube is
146    /// [0, 0, 0]
147    /// [1, 0, 0]
148    /// [1, 1, 0]
149    /// [0, 1, 0]
150    /// [0, 0, 1]
151    /// [1, 0, 1]
152    /// [1, 1, 1]
153    /// [0, 1, 1]
154    pub fn corners(&self) -> [[f64; 3]; 8] {
155        let reference_points = [
156            [0.0, 0.0, 0.0],
157            [1.0, 0.0, 0.0],
158            [1.0, 1.0, 0.0],
159            [0.0, 1.0, 0.0],
160            [0.0, 0.0, 1.0],
161            [1.0, 0.0, 1.0],
162            [1.0, 1.0, 1.0],
163            [0.0, 1.0, 1.0],
164        ];
165
166        [
167            self.reference_to_physical(reference_points[0]),
168            self.reference_to_physical(reference_points[1]),
169            self.reference_to_physical(reference_points[2]),
170            self.reference_to_physical(reference_points[3]),
171            self.reference_to_physical(reference_points[4]),
172            self.reference_to_physical(reference_points[5]),
173            self.reference_to_physical(reference_points[6]),
174            self.reference_to_physical(reference_points[7]),
175        ]
176    }
177}
178
179impl std::fmt::Display for PhysicalBox {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        let [xmin, ymin, zmin, xmax, ymax, zmax] = self.coords;
182
183        write!(
184            f,
185            "(xmin: {}, ymin: {}, zmin: {}, xmax: {}, ymax: {}, zmax: {})",
186            xmin, ymin, zmin, xmax, ymax, zmax
187        )
188    }
189}