geometry_kernel/
precision.rs1use crate::types::{Coord, LineString, LinearRing, MultiPolygon, Polygon};
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub struct PrecisionModel {
5 grid_size: Option<f64>,
6 epsilon: f64,
7}
8
9impl PrecisionModel {
10 pub const fn floating() -> Self {
11 Self {
12 grid_size: None,
13 epsilon: 1.0e-9,
14 }
15 }
16
17 pub fn fixed(grid_size: f64) -> Self {
18 assert!(grid_size.is_finite() && grid_size > 0.0);
19 Self {
20 grid_size: Some(grid_size),
21 epsilon: grid_size * 0.5,
22 }
23 }
24
25 pub const fn epsilon(self) -> f64 {
26 self.epsilon
27 }
28
29 pub fn snap_value(self, value: f64) -> f64 {
30 match self.grid_size {
31 Some(grid) => (value / grid).round() * grid,
32 None => value,
33 }
34 }
35
36 pub fn snap_coord(self, coord: Coord) -> Coord {
37 Coord::new(self.snap_value(coord.x), self.snap_value(coord.y))
38 }
39
40 pub fn same_coord(self, a: Coord, b: Coord) -> bool {
41 a.distance_squared(b) <= self.epsilon * self.epsilon
42 }
43
44 pub fn snap_line(self, line: &LineString) -> LineString {
45 LineString::new(line.coords.iter().map(|c| self.snap_coord(*c)).collect())
46 }
47
48 pub fn snap_ring(self, ring: &LinearRing) -> LinearRing {
49 LinearRing::new(ring.coords.iter().map(|c| self.snap_coord(*c)).collect())
50 }
51
52 pub fn snap_polygon(self, polygon: &Polygon) -> Polygon {
53 Polygon::new(
54 self.snap_ring(&polygon.exterior),
55 polygon
56 .holes
57 .iter()
58 .map(|ring| self.snap_ring(ring))
59 .collect(),
60 )
61 }
62
63 pub fn snap_multi_polygon(self, multi_polygon: &MultiPolygon) -> MultiPolygon {
64 MultiPolygon::new(
65 multi_polygon
66 .polygons
67 .iter()
68 .map(|polygon| self.snap_polygon(polygon))
69 .collect(),
70 )
71 }
72}
73
74impl Default for PrecisionModel {
75 fn default() -> Self {
76 Self::floating()
77 }
78}