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: &'static str,
28
29}
30
31impl GRID1D {
32    pub const 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:"",
48        }
49    }
50    pub fn num(&self)-> usize{
51        //TODO MUST VERIFY THI IS AN INTEGER
52        ( (self.maximum_value-self.minimum_value)/self.step_size ) as usize + 1
53    }
54
55
56    pub fn location(&self, grid_number:usize) -> f64 {
57        assert!((grid_number <= self.num()-1)&&(grid_number >= 0));
58        self.minimum_value + self.step_size*grid_number as f64
59    }
60
61    pub fn size(&self)-> f64{
62        self.step_size*(self.num()-1)as f64
63    }
64
65    pub fn random(&self)-> f64{
66        let mut rng = rand::rng();
67        let scale: f64 = rng.random();
68        self.minimum_value + self.size()*scale
69
70    }
71    pub fn inside_or_outside(&self, point:f64) -> Location1D{
72        let epsilon = self.snap_precision;
73        let max = self.minimum_value + self.size();
74        if (point < self.minimum_value - epsilon) {
75            return Location1D::TooLow
76        };
77        if (point > max + epsilon){
78            return Location1D::TooHigh
79        }
80        Location1D::JustRight
81    }
82
83    pub fn fit_grid(&self, point:f64)->(usize,f64){
84        //ensure that the point is within the grid
85        match self.inside_or_outside(point) {
86            Location1D::TooHigh => {panic!("too big to fit")}
87            Location1D::TooLow => {panic!("too small to fit")}
88            Location1D::JustRight => {}
89        }
90        //find the nearest point and then return the residuals to it
91        let delta = point - self.minimum_value;
92        let scaled_residual = delta/self.step_size - (delta/self.step_size).floor();
93
94
95        let (modulus, residual) = if scaled_residual <= 0.5{
96            let modulus = (delta/self.step_size).floor() as usize;
97            let residual = scaled_residual*self.step_size;
98            (modulus,residual)
99
100        }else{
101            let modulus = (delta/self.step_size).floor() as usize + 1;
102            let residual = (scaled_residual-1.0)*self.step_size;
103            (modulus,residual)
104        };
105
106        (modulus, residual)
107    }
108    //TODO remove redundancey of these two functions
109
110    pub fn snap(&self,point:f64)-> usize{
111        let (modulus,residual) = self.fit_grid(point);
112        if (residual.abs() >= self.snap_precision){
113            panic!("Couldn't snap point")
114        };
115        modulus
116    }
117
118
119    pub fn find_neighbors(&self, point:f64) -> Neighbors{
120        let epsilon = self.snap_precision;
121        let (modulus,residual)  = self.fit_grid(point);
122        if (residual.abs() <= epsilon){
123            return Neighbors::One(self.snap(point))
124        };
125        //The point must be in the middle
126        let (upper,lower) = if residual < 0.0{ (modulus,modulus-1) }else{ (modulus+1, modulus)};
127        Neighbors::Two(upper,lower)
128    }
129
130    pub fn plot_points(&self, plot:&mut Plot, add_point:PlotPoint){
131
132        let mut frame = Curve::new();
133        frame.set_marker_color("pink")
134            .set_marker_every(1)
135            .set_marker_style(".");
136
137        let mut grid_points = Curve::new();
138        grid_points.set_line_style("none")
139            .set_label(format!("Grid points: {:?}",self.label).as_str())
140            .set_marker_color("blue")
141            .set_marker_every(1)
142            .set_marker_size(7.0)
143            .set_marker_style(".");
144
145        let mut corner = Curve::new();
146        corner
147            .set_label("Corner")
148            .set_line_style("none")
149            .set_marker_color("#eeea83")
150            .set_marker_every(1)
151            .set_marker_size(10.0)
152            .set_marker_style(".");
153
154
155        let mut extra_point = Curve::new();
156        extra_point.set_marker_color("#eeea83")
157            .set_marker_every(1)
158            .set_marker_size(10.0)
159            .set_line_style("none")
160            .set_marker_style("*");
161
162
163
164        let mut grid_numbers = Text::new();
165        grid_numbers.set_color("purple")
166            .set_fontsize(5.0);
167
168
169        grid_points.points_begin();
170        for point in 0..self.num(){
171            let point_location = self.location(point);
172            grid_points.points_add(point_location, 0.0);
173            let label = format!("{}",point);
174            grid_numbers.draw(point_location, 0.0, label.as_str());
175        }
176        grid_points.points_end();
177
178        corner.points_begin();
179        let corner_location = self.minimum_value;
180        let corner_label = format!("Corner: ({:.3},{:.3})",corner_location,0.0);
181        corner.points_add(corner_location,0.0).set_label(corner_label.as_str());
182        corner.points_end();
183
184        let mut example_point = Vec::new();
185
186        match add_point {
187            PlotPoint::No => {}
188            PlotPoint::Given(point) => { example_point.push(point.x)}
189            PlotPoint::Random => {
190                let random = self.random();
191                example_point.push((random)); }
192        };
193
194        for point in example_point{
195            let (_,res) = self.fit_grid(point);
196            extra_point.points_begin();
197            extra_point.points_add(point,0.0).set_label(format!("x, y residuals: {:.3}", res).as_str());
198            extra_point.points_end();
199
200            let corners  = self.find_neighbors(point);
201            let mut frame_points = Vec::new();
202            match corners{
203
204                Neighbors::Two(a,b) => {
205                    frame_points.push(a);
206                    frame_points.push(b);
207                    println!("{:?}",(a,b));}
208
209                Neighbors::One(a) => { frame_points.push(a); }
210            }
211            frame.points_begin();
212            for point in frame_points{
213                println!("point {point}");
214                let point = self.location(point);
215                frame.points_add(point,0.0);
216            }
217
218            frame.points_end();
219        }
220
221
222
223        plot.add(&grid_numbers);
224        plot.add(&grid_points);
225        plot.add(&extra_point);
226        plot.add(&corner);
227        plot.add(&frame)
228            .set_figure_size_inches(10.0,10.0)
229            .grid_labels_legend("x", "y");
230
231
232    }
233
234}
235
236
237