rust_3d/
point_cloud_2d.rs

1/*
2Copyright 2016 Martin Buck
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation the
7rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the Software
9is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall
12be included all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23//! PointCloud2D, a collection of positions within 2D space
24
25use std::{
26    fmt,
27    ops::{Index, IndexMut},
28};
29
30use crate::*;
31
32//------------------------------------------------------------------------------
33
34#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
35/// PointCloud2D, a collection of positions within 2D space
36pub struct PointCloud2D<P>
37where
38    P: Is2D,
39{
40    pub data: Vec<P>,
41}
42
43impl<P> PointCloud2D<P>
44where
45    P: Is2D,
46{
47    /// Creates a new, empty point cloud
48    pub fn new() -> PointCloud2D<P> {
49        PointCloud2D { data: Vec::new() }
50    }
51    /// Creates a new, empty point cloud with capacity
52    pub fn with_capacity(n: usize) -> PointCloud2D<P> {
53        PointCloud2D {
54            data: Vec::with_capacity(n),
55        }
56    }
57    /// Serializes the point cloud
58    pub fn to_str(&self) -> String {
59        let mut result = String::new();
60        for p in &self.data {
61            result = result + &p.to_str() + "\n";
62        }
63        result
64    }
65    /// Applies a function to each position
66    pub fn for_each_point<F>(&mut self, mut f: F)
67    where
68        F: FnMut(&mut P),
69    {
70        for p in &mut self.data {
71            f(&mut *p);
72        }
73    }
74    /// Reserves number of vertices
75    pub fn reserve_vertices(&mut self, n: usize) {
76        self.data.reserve(n)
77    }
78}
79
80impl<P> PointCloud2D<P>
81where
82    P: IsBuildable2D + Clone,
83{
84    /// Creates a new point cloud from an input string
85    pub fn parse(text: &str) -> Result<PointCloud2D<P>> {
86        let lines = text.split("\n").skip_empty_string();
87
88        let mut pc = PointCloud2D::new();
89        for line in lines {
90            P::parse(line).map(|p| pc.push(p))?;
91        }
92        if pc.len() == 0 {
93            return Err(ErrorKind::ParseError);
94        }
95        Ok(pc)
96    }
97    /// Appends all elements of an IsRandomAccessible
98    pub fn append_ra<RA>(&mut self, ra: &RA)
99    where
100        RA: IsRandomAccessible<P>,
101    {
102        let n = ra.len();
103        self.data.reserve(n);
104
105        for i in 0..n {
106            self.data.push(ra[i].clone());
107        }
108    }
109}
110
111//------------------------------------------------------------------------------
112
113impl<P> IsDataContainer<P> for PointCloud2D<P>
114where
115    P: IsBuildable2D + Clone,
116{
117    fn reserve_d(&mut self, n: usize) {
118        self.data.reserve(n);
119    }
120
121    fn len_d(&self) -> usize {
122        self.data.len()
123    }
124
125    fn push_d(&mut self, p: P) {
126        self.push(p)
127    }
128
129    fn get_d(&self, index: usize) -> P {
130        self.data[index].clone()
131    }
132
133    fn set_d(&mut self, index: usize, p: P) {
134        self.data[index] = p
135    }
136}
137
138impl<P> Index<usize> for PointCloud2D<P>
139where
140    P: Is2D,
141{
142    type Output = P;
143    fn index(&self, i: usize) -> &P {
144        &self.data[i]
145    }
146}
147
148impl<P> IndexMut<usize> for PointCloud2D<P>
149where
150    P: Is2D,
151{
152    fn index_mut(&mut self, i: usize) -> &mut P {
153        &mut self.data[i]
154    }
155}
156
157impl<P> IsRandomAccessible<P> for PointCloud2D<P>
158where
159    P: Is2D,
160{
161    fn len(&self) -> usize {
162        self.data.len()
163    }
164}
165
166impl<P> IsRandomInsertible<P> for PointCloud2D<P>
167where
168    P: Is2D,
169{
170    fn insert(&mut self, index: usize, point: P) -> Result<()> {
171        if index > self.len() {
172            Err(ErrorKind::IncorrectVertexID)
173        } else {
174            self.data.insert(index, point);
175            Ok(())
176        }
177    }
178}
179
180impl<P> IsPushable<P> for PointCloud2D<P>
181where
182    P: Is2D,
183{
184    fn push(&mut self, point: P) {
185        self.data.push(point)
186    }
187    fn reserve(&mut self, n: usize) {
188        self.data.reserve(n)
189    }
190}
191
192impl<P> IsMovable2D for PointCloud2D<P>
193where
194    P: Is2D + IsMovable2D,
195{
196    fn move_by(&mut self, x: f64, y: f64) {
197        for p in &mut self.data {
198            p.move_by(x, y);
199        }
200    }
201}
202
203impl<P> HasBoundingBox2DMaybe for PointCloud2D<P>
204where
205    P: Is2D,
206{
207    fn bounding_box_maybe(&self) -> Result<BoundingBox2D> {
208        BoundingBox2D::from_iterator(&self.data)
209    }
210}
211
212impl<P> HasCenterOfGravity2D for PointCloud2D<P>
213where
214    P: Is2D,
215{
216    fn center_of_gravity(&self) -> Result<Point2D> {
217        let size = self.len();
218
219        if size < 1 {
220            return Err(ErrorKind::TooFewPoints);
221        }
222
223        let sizef = size as f64;
224
225        let mut sumx: f64 = 0.0;
226        let mut sumy: f64 = 0.0;
227
228        for p in &self.data {
229            sumx += p.x();
230            sumy += p.y();
231        }
232
233        Ok(Point2D {
234            x: sumx / sizef,
235            y: sumy / sizef,
236        })
237    }
238}
239
240impl<P> HasLength for PointCloud2D<P>
241where
242    P: Is2D,
243{
244    fn length(&self) -> f64 {
245        let mut length: f64 = 0.0;
246        if self.data.len() < 2 {
247            return length;
248        }
249
250        for i in 1..self.data.len() {
251            length += dist_2d(&self.data[i], &self.data[i - 1]);
252        }
253        length
254    }
255}
256
257impl<P> IsViewBuildable for PointCloud2D<P>
258where
259    P: Is2D + Clone,
260{
261    fn apply_view(&mut self, view: &View) -> Result<()> {
262        self.data.apply_view(view)?;
263        Ok(())
264    }
265
266    fn from_view(&self, view: &View) -> Result<Self> {
267        let mut cloned = self.clone();
268        cloned.apply_view(view)?;
269        Ok(cloned)
270    }
271}
272
273impl<P> IsSortableND for PointCloud2D<P>
274where
275    P: Is2D,
276{
277    fn n_dimensions() -> usize {
278        2
279    }
280
281    fn sort_dim(&mut self, dimension: usize) -> Result<()> {
282        match dimension {
283            0 => {
284                self.sort_x();
285                Ok(())
286            }
287            1 => {
288                self.sort_y();
289                Ok(())
290            }
291            _ => Err(ErrorKind::IncorrectDimension),
292        }
293    }
294}
295
296impl<P> IsSortable2D for PointCloud2D<P>
297where
298    P: Is2D,
299{
300    fn sort_x(&mut self) {
301        sort_vec_2d_x(&mut self.data)
302    }
303
304    fn sort_y(&mut self) {
305        sort_vec_2d_y(&mut self.data)
306    }
307}
308
309impl<P> IsMergeable for PointCloud2D<P>
310where
311    P: Is2D + Clone,
312{
313    fn consume(&mut self, other: Self) {
314        for p in other.data {
315            self.data.push(p.clone());
316        }
317    }
318
319    fn combine(&self, other: &Self) -> Self {
320        let mut result = self.clone();
321        result.consume(other.clone());
322        result
323    }
324}
325
326impl<P> IsScalable for PointCloud2D<P>
327where
328    P: IsEditable2D,
329{
330    fn scale(&mut self, factor: Positive) {
331        if let Ok(bb) = self.bounding_box_maybe() {
332            let c = bb.center_bb();
333            for p in &mut self.data {
334                p.increase_distance_to_by(&c, factor);
335            }
336        }
337    }
338}
339
340impl<P> IsMatrix3Transformable for PointCloud2D<P>
341where
342    P: Is2D + IsMatrix3Transformable + Clone,
343{
344    fn transformed(&self, m: &Matrix3) -> Self {
345        let mut new = self.clone();
346        new.transform(m);
347        new
348    }
349
350    fn transform(&mut self, m: &Matrix3) {
351        for p in &mut self.data {
352            p.transform(m);
353        }
354    }
355}
356
357impl<P> Default for PointCloud2D<P>
358where
359    //https://github.com/rust-lang/rust/issues/26925
360    P: Is2D,
361{
362    fn default() -> Self {
363        let data = Vec::new();
364        PointCloud2D { data }
365    }
366}
367
368impl<P> fmt::Display for PointCloud2D<P>
369where
370    P: Is2D + fmt::Display,
371{
372    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
373        for p in &self.data {
374            p.fmt(f)?;
375            f.write_str("\n")?;
376        }
377        Ok(())
378    }
379}
380
381impl<P> Into<Vec<P>> for PointCloud2D<P>
382where
383    P: Is2D,
384{
385    fn into(self) -> Vec<P> {
386        self.data
387    }
388}
389
390impl<P> From<Vec<P>> for PointCloud2D<P>
391where
392    P: Is2D,
393{
394    fn from(data: Vec<P>) -> Self {
395        Self { data }
396    }
397}