Skip to main content

geos/
buffer_params.rs

1use crate::context_handle::with_context;
2use crate::enums::CapStyle;
3use crate::functions::{errcheck, nullcheck};
4use crate::traits::as_raw_mut_impl;
5use crate::{AsRaw, AsRawMut, GResult, JoinStyle};
6use geos_sys::*;
7use std::ptr::NonNull;
8
9/// Contains the parameters which describe how a [Geometry](crate::Geometry) buffer should be constructed using [`buffer_with_params`](crate::Geom::buffer_with_params)
10pub struct BufferParams {
11    ptr: NonNull<GEOSBufferParams>,
12}
13
14/// Build options for a [`BufferParams`] object
15#[derive(Default)]
16pub struct BufferParamsBuilder {
17    end_cap_style: Option<CapStyle>,
18    join_style: Option<JoinStyle>,
19    mitre_limit: Option<f64>,
20    quadrant_segments: Option<i32>,
21    single_sided: Option<bool>,
22}
23
24impl BufferParams {
25    pub fn new() -> GResult<Self> {
26        with_context(|ctx| unsafe {
27            let ptr = nullcheck!(GEOSBufferParams_create_r(ctx.as_raw()))?;
28            Ok(Self { ptr })
29        })
30    }
31
32    pub fn builder() -> BufferParamsBuilder {
33        BufferParamsBuilder::default()
34    }
35
36    /// Specifies the end cap style of the generated buffer.
37    pub fn set_end_cap_style(&mut self, style: CapStyle) -> GResult<()> {
38        with_context(|ctx| unsafe {
39            errcheck!(GEOSBufferParams_setEndCapStyle_r(
40                ctx.as_raw(),
41                self.as_raw_mut_override(),
42                style.into(),
43            ))?;
44            Ok(())
45        })
46    }
47
48    /// Sets the join style for outside (reflex) corners between line segments.
49    pub fn set_join_style(&mut self, style: JoinStyle) -> GResult<()> {
50        with_context(|ctx| unsafe {
51            errcheck!(GEOSBufferParams_setJoinStyle_r(
52                ctx.as_raw(),
53                self.as_raw_mut_override(),
54                style.into(),
55            ))?;
56            Ok(())
57        })
58    }
59
60    /// Sets the limit on the mitre ratio used for very sharp corners.
61    ///
62    /// The mitre ratio is the ratio of the distance from the corner
63    /// to the end of the mitred offset corner.
64    /// When two line segments meet at a sharp angle,
65    /// a miter join will extend far beyond the original geometry.
66    /// (and in the extreme case will be infinitely far.)
67    /// To prevent unreasonable geometry, the mitre limit
68    /// allows controlling the maximum length of the join corner.
69    /// Corners with a ratio which exceed the limit will be beveled.
70    pub fn set_mitre_limit(&mut self, limit: f64) -> GResult<()> {
71        with_context(|ctx| unsafe {
72            errcheck!(GEOSBufferParams_setMitreLimit_r(
73                ctx.as_raw(),
74                self.as_raw_mut_override(),
75                limit
76            ))?;
77            Ok(())
78        })
79    }
80
81    /// Sets the number of line segments used to approximate
82    /// an angle fillet.
83    ///
84    /// - If `quadsegs` >= 1, joins are round, and `quadsegs` indicates the number of
85    ///   segments to use to approximate a quarter-circle.
86    /// - If `quadsegs` = 0, joins are bevelled (flat)
87    /// - If `quadSegs` < 0, joins are mitred, and the value of qs
88    ///   indicates the mitre ration limit as `mitreLimit = |quadsegs|`
89    ///
90    /// For round joins, `quadsegs` determines the maximum
91    /// error in the approximation to the true buffer curve.
92    ///
93    /// The default value of 8 gives less than 2% max error in the
94    /// buffer distance.
95    ///
96    /// For a max error of < 1%, use QS = 12.
97    /// For a max error of < 0.1%, use QS = 18.
98    /// The error is always less than the buffer distance
99    /// (in other words, the computed buffer curve is always inside
100    ///  the true curve).
101    pub fn set_quadrant_segments(&mut self, quadsegs: i32) -> GResult<()> {
102        with_context(|ctx| unsafe {
103            errcheck!(GEOSBufferParams_setQuadrantSegments_r(
104                ctx.as_raw(),
105                self.as_raw_mut_override(),
106                quadsegs as _,
107            ))?;
108            Ok(())
109        })
110    }
111
112    /// Sets whether the computed buffer should be single-sided.
113    ///
114    /// A single-sided buffer is constructed on only one side of each input line.
115    ///
116    /// The side used is determined by the sign of the buffer distance:
117    /// - a positive distance indicates the left-hand side
118    /// - a negative distance indicates the right-hand side
119    ///
120    /// The single-sided buffer of point geometries is the same as the regular buffer.
121    ///
122    /// The End Cap Style for single-sided buffers is always ignored, and forced to the
123    /// equivalent of [`CapStyle::Flat`].
124    pub fn set_single_sided(&mut self, is_single_sided: bool) -> GResult<()> {
125        with_context(|ctx| unsafe {
126            errcheck!(GEOSBufferParams_setSingleSided_r(
127                ctx.as_raw(),
128                self.as_raw_mut_override(),
129                is_single_sided.into(),
130            ))?;
131            Ok(())
132        })
133    }
134}
135
136unsafe impl Send for BufferParams {}
137unsafe impl Sync for BufferParams {}
138
139impl Drop for BufferParams {
140    fn drop(&mut self) {
141        with_context(|ctx| unsafe { GEOSBufferParams_destroy_r(ctx.as_raw(), self.as_raw_mut()) });
142    }
143}
144
145as_raw_mut_impl!(BufferParams, GEOSBufferParams);
146
147impl BufferParamsBuilder {
148    pub const fn end_cap_style(mut self, style: CapStyle) -> Self {
149        self.end_cap_style = Some(style);
150        self
151    }
152    pub const fn join_style(mut self, style: JoinStyle) -> Self {
153        self.join_style = Some(style);
154        self
155    }
156    pub const fn mitre_limit(mut self, limit: f64) -> Self {
157        self.mitre_limit = Some(limit);
158        self
159    }
160    pub const fn quadrant_segments(mut self, quadsegs: i32) -> Self {
161        self.quadrant_segments = Some(quadsegs);
162        self
163    }
164    pub const fn single_sided(mut self, is_single_sided: bool) -> Self {
165        self.single_sided = Some(is_single_sided);
166        self
167    }
168    pub fn build(self) -> GResult<BufferParams> {
169        let mut params = BufferParams::new()?;
170        if let Some(style) = self.end_cap_style {
171            params.set_end_cap_style(style)?;
172        }
173        if let Some(style) = self.join_style {
174            params.set_join_style(style)?;
175        }
176        if let Some(limit) = self.mitre_limit {
177            params.set_mitre_limit(limit)?;
178        }
179        if let Some(quad_segs) = self.quadrant_segments {
180            params.set_quadrant_segments(quad_segs)?;
181        }
182        if let Some(is_single_sided) = self.single_sided {
183            params.set_single_sided(is_single_sided)?;
184        }
185        Ok(params)
186    }
187}