Skip to main content

astroimsim_geometry/
grid1d.rs

1use plotpy::{Curve, Plot, Text};
2use crate::coordinate_system::{CoordinateSystem, Coordinates};
3use rand::RngExt;
4use crate::grid2d::PlotPoint;
5use crate::points::Point;
6
7
8
9pub enum Location1D{
10    TooHigh,
11    TooLow,
12    JustRight,
13}
14
15pub enum Neighbors {
16    Two(usize,usize),
17    One(usize),
18}
19
20#[derive(Debug,Clone)]
21pub struct GRID1D {
22    pub scale: f64,
23    pub step_size: f64,
24    pub minimum_value: f64,
25    pub maximum_value: f64,
26    pub snap_precision: f64,
27    pub label: String,
28
29}
30
31impl GRID1D {
32    pub fn new_empty(
33        step_size: f64, //TODO have units for this length
34        minimum_value: f64,
35        maximum_value: f64,
36        snap_precision: f64,
37        scale:f64,
38
39    ) -> GRID1D {
40        assert!(snap_precision<0.5);
41        GRID1D {
42            scale,
43            step_size,
44            minimum_value,
45            maximum_value,
46            snap_precision,
47            label:"".to_string(),
48        }
49    }
50
51
52    pub fn new_from_points(points:Vec<f64>,snap_precision:f64,label:String)-> GRID1D{
53        //we try to make a regular grid out of a vector of points
54        let num = points.len();
55        assert!(num>=2, "Need at least 2 points to make a grid");
56        let high = points[num-1];
57        let low = points[0];
58        assert!(low < high, "First point is not lower than last point");
59        let expected_interval = (high-low)/(num as f64 -1.0);
60        (0..num-1).for_each(|i|{
61            assert!(points[i+1]>points[i],
62                    "Failed to make grid because points must monotonically increase");
63            assert!(((points[i+1]-points[i]) - expected_interval).abs() < snap_precision*expected_interval,
64                    " Failed to make grid because points must be evenly spaced")
65        });
66        GRID1D{
67            scale: 1.0,
68            step_size: expected_interval,
69            minimum_value: low,
70            maximum_value: high,
71            snap_precision,
72            label,
73        }
74    }
75
76    pub fn pretty_print(&self){
77        println!("1D grid {:?} \n\
78        min: {} \n\
79        max: {} \n\
80        step size: {} \n\
81        snap precision: {} \n",
82        self.label,
83        self.minimum_value,
84        self.maximum_value,
85        self.step_size,
86        self.snap_precision)
87    }
88    pub fn num(&self)-> usize{
89        //TODO MUST VERIFY THI IS AN INTEGER
90        ( (self.maximum_value-self.minimum_value)/self.step_size ) as usize + 1
91    }
92
93
94    pub fn location(&self, grid_number:usize) -> f64 {
95        assert!((grid_number <= self.num()-1)&&(grid_number >= 0));
96        self.minimum_value + self.step_size*grid_number as f64
97    }
98
99    pub fn size(&self)-> f64{
100        self.step_size*(self.num()-1)as f64
101    }
102
103    pub fn random(&self)-> f64{
104        let mut rng = rand::rng();
105        let scale: f64 = rng.random();
106        self.minimum_value + self.size()*scale
107
108    }
109    pub fn inside_or_outside(&self, point:f64) -> Location1D{
110        let epsilon = self.snap_precision;
111        let max = self.minimum_value + self.size();
112        if (point < self.minimum_value - epsilon) {
113            return Location1D::TooLow
114        };
115        if (point > max + epsilon){
116            return Location1D::TooHigh
117        }
118        Location1D::JustRight
119    }
120
121    pub fn fit_grid(&self, point:f64)->(usize,f64){
122        //ensure that the point is within the grid
123        match self.inside_or_outside(point) {
124            Location1D::TooHigh => {panic!("too big to fit")}
125            Location1D::TooLow => {panic!("too small to fit")}
126            Location1D::JustRight => {}
127        }
128        //find the nearest point and then return the residuals to it
129        let delta = point - self.minimum_value;
130        let scaled_residual = delta/self.step_size - (delta/self.step_size).floor();
131
132
133        let (modulus, residual) = if scaled_residual <= 0.5{
134            let modulus = (delta/self.step_size).floor() as usize;
135            let residual = scaled_residual*self.step_size;
136            (modulus,residual)
137
138        }else{
139            let modulus = (delta/self.step_size).floor() as usize + 1;
140            let residual = (scaled_residual-1.0)*self.step_size;
141            (modulus,residual)
142        };
143
144        (modulus, residual)
145    }
146    //TODO remove redundancey of these two functions
147
148    pub fn snap(&self,point:f64)-> usize{
149        let (modulus,residual) = self.fit_grid(point);
150        if (residual.abs() >= self.snap_precision){
151            panic!("Couldn't snap point")
152        };
153        modulus
154    }
155
156
157    pub fn find_neighbors(&self, point:f64) -> Neighbors{
158        let epsilon = self.snap_precision;
159        let (modulus,residual)  = self.fit_grid(point);
160        if (residual.abs() <= epsilon){
161            return Neighbors::One(self.snap(point))
162        };
163        //The point must be in the middle
164        let (upper,lower) = if residual < 0.0{ (modulus,modulus-1) }else{ (modulus+1, modulus)};
165        Neighbors::Two(upper,lower)
166    }
167
168    pub fn plot_points(&self, plot:&mut Plot, add_point:PlotPoint){
169
170        let mut frame = Curve::new();
171        frame.set_marker_color("pink")
172            .set_marker_every(1)
173            .set_marker_style(".");
174
175        let mut grid_points = Curve::new();
176        grid_points.set_line_style("none")
177            .set_label(format!("Grid points: {:?}",self.label).as_str())
178            .set_marker_color("blue")
179            .set_marker_every(1)
180            .set_marker_size(7.0)
181            .set_marker_style(".");
182
183        let mut corner = Curve::new();
184        corner
185            .set_label("Corner")
186            .set_line_style("none")
187            .set_marker_color("#eeea83")
188            .set_marker_every(1)
189            .set_marker_size(10.0)
190            .set_marker_style(".");
191
192
193        let mut extra_point = Curve::new();
194        extra_point.set_marker_color("#eeea83")
195            .set_marker_every(1)
196            .set_marker_size(10.0)
197            .set_line_style("none")
198            .set_marker_style("*");
199
200
201
202        let mut grid_numbers = Text::new();
203        grid_numbers.set_color("purple")
204            .set_fontsize(5.0);
205
206
207        grid_points.points_begin();
208        for point in 0..self.num(){
209            let point_location = self.location(point);
210            grid_points.points_add(point_location, 0.0);
211            let label = format!("{}",point);
212            grid_numbers.draw(point_location, 0.0, label.as_str());
213        }
214        grid_points.points_end();
215
216        corner.points_begin();
217        let corner_location = self.minimum_value;
218        let corner_label = format!("Corner: ({:.3},{:.3})",corner_location,0.0);
219        corner.points_add(corner_location,0.0).set_label(corner_label.as_str());
220        corner.points_end();
221
222        let mut example_point = Vec::new();
223
224        match add_point {
225            PlotPoint::No => {}
226            PlotPoint::Given(point) => { example_point.push(point.x)}
227            PlotPoint::Random => {
228                let random = self.random();
229                example_point.push((random)); }
230        };
231
232        for point in example_point{
233            let (_,res) = self.fit_grid(point);
234            extra_point.points_begin();
235            extra_point.points_add(point,0.0).set_label(format!("x, y residuals: {:.3}", res).as_str());
236            extra_point.points_end();
237
238            let corners  = self.find_neighbors(point);
239            let mut frame_points = Vec::new();
240            match corners{
241
242                Neighbors::Two(a,b) => {
243                    frame_points.push(a);
244                    frame_points.push(b);
245                    println!("{:?}",(a,b));}
246
247                Neighbors::One(a) => { frame_points.push(a); }
248            }
249            frame.points_begin();
250            for point in frame_points{
251                println!("point {point}");
252                let point = self.location(point);
253                frame.points_add(point,0.0);
254            }
255
256            frame.points_end();
257        }
258
259
260
261        plot.add(&grid_numbers);
262        plot.add(&grid_points);
263        plot.add(&extra_point);
264        plot.add(&corner);
265        plot.add(&frame)
266            .set_figure_size_inches(10.0,10.0)
267            .grid_labels_legend("x", "y");
268
269
270    }
271
272}
273
274
275