gridsim_ui/
run.rs

1use crate::Renderer;
2use glium::{
3    self,
4    glutin::{self, WindowEvent},
5};
6use gridsim::{GetNeighbors, Sim, SquareGrid, TakeMoveNeighbors};
7
8#[cfg(feature = "multinode")]
9use serde::{Deserialize, Serialize};
10#[cfg(feature = "multinode")]
11use std::io::{Read, Write};
12
13type Coloration<C> = Box<dyn Fn(&C) -> [f32; 3] + Sync>;
14type Filter<C> = Box<dyn Fn(&C) -> bool + Sync>;
15
16pub struct Loop<'a, S>
17where
18    S: Sim<'a>,
19{
20    scale: f32,
21    coloration: Coloration<S::Cell>,
22    filter: Filter<S::Cell>,
23}
24
25impl<'a, S> Loop<'a, S>
26where
27    S: Sim<'a>,
28{
29    /// Must pass the function that colors cells. Color is in RGB (`[red, green, blue]`).
30    pub fn new<C>(coloration: C) -> Self
31    where
32        C: Fn(&S::Cell) -> [f32; 3] + Sync + 'static,
33    {
34        Loop {
35            scale: 10.0,
36            coloration: Box::new(coloration),
37            filter: Box::new(|_| true),
38        }
39    }
40
41    pub fn new_bool() -> Self
42    where
43        S: Sim<'a, Cell = bool>,
44    {
45        Loop {
46            scale: 10.0,
47            coloration: Box::new(|_| [1.0, 1.0, 1.0]),
48            filter: Box::new(|&c| c),
49        }
50    }
51
52    pub fn scale(&mut self, scale: f32) -> &mut Self {
53        self.scale = scale;
54        self
55    }
56
57    pub fn filter<F>(&mut self, filter: F) -> &mut Self
58    where
59        F: Fn(&S::Cell) -> bool + Sync + 'static,
60    {
61        self.filter = Box::new(filter);
62        self
63    }
64
65    pub fn run(&self, mut grid: SquareGrid<'a, S>)
66    where
67        S: 'a,
68        S::Cell: Sync + Send,
69        S::Move: Sync + Send,
70        S::Diff: Sync + Send,
71        S::Neighbors: Sync + Send,
72        S::MoveNeighbors: Sync + Send,
73        SquareGrid<'a, S>: TakeMoveNeighbors<usize, S::MoveNeighbors>,
74        SquareGrid<'a, S>: GetNeighbors<'a, usize, S::Neighbors>,
75    {
76        let mut events_loop = glutin::EventsLoop::new();
77        let window = glutin::WindowBuilder::new().with_dimensions(glutin::dpi::LogicalSize::new(
78            f64::from(self.scale) * grid.get_width() as f64,
79            f64::from(self.scale) * grid.get_height() as f64,
80        ));
81        let context = glutin::ContextBuilder::new().with_vsync(true);
82        let display = glium::Display::new(window, context, &events_loop).unwrap();
83        let renderer = Renderer::new(&display);
84
85        loop {
86            use glium::Surface;
87            grid.cycle();
88
89            let mut target = display.draw();
90            target.clear_color(0.0, 0.0, 0.0, 1.0);
91            renderer
92                .render(
93                    &display,
94                    &mut target,
95                    &grid,
96                    Default::default(),
97                    &*self.coloration,
98                    &*self.filter,
99                )
100                .unwrap();
101            target.finish().unwrap();
102
103            let mut finish = false;
104
105            // the main loop
106            events_loop.poll_events(|event| {
107                if let glutin::Event::WindowEvent {
108                    event: WindowEvent::CloseRequested,
109                    ..
110                } = event
111                {
112                    // Break from the main loop when the window is closed.
113                    finish = true;
114                }
115            });
116
117            if finish {
118                return;
119            }
120        }
121    }
122
123    /// Runs serializing and deserializing to and from another `run_multi`. As soon as any read or write fails
124    /// this will terminate without prompting.
125    ///
126    /// Make sure the reads and writes are only connected to other `SquareGrid::run_multi` running
127    /// on any machine using THE EXACT SAME simulation or else there may be undefined behavior.
128    #[cfg(feature = "multinode")]
129    #[allow(clippy::too_many_arguments)]
130    pub unsafe fn run_multi<
131        I0: Read,
132        I1: Read,
133        I2: Read,
134        I3: Read,
135        I4: Read,
136        I5: Read,
137        I6: Read,
138        I7: Read,
139        O0: Write,
140        O1: Write,
141        O2: Write,
142        O3: Write,
143        O4: Write,
144        O5: Write,
145        O6: Write,
146        O7: Write,
147    >(
148        &self,
149        mut grid: SquareGrid<'a, S>,
150        mut in_right: I0,
151        mut in_up_right: I1,
152        mut in_up: I2,
153        mut in_up_left: I3,
154        mut in_left: I4,
155        mut in_down_left: I5,
156        mut in_down: I6,
157        mut in_down_right: I7,
158        mut out_right: O0,
159        mut out_up_right: O1,
160        mut out_up: O2,
161        mut out_up_left: O3,
162        mut out_left: O4,
163        mut out_down_left: O5,
164        mut out_down: O6,
165        mut out_down_right: O7,
166    ) where
167        S: 'a,
168        for<'dc> S::Cell: Sync + Send + Serialize + Deserialize<'dc>,
169        S::Move: Sync + Send,
170        S::Diff: Sync + Send,
171        S::Neighbors: Sync + Send,
172        S::MoveNeighbors: Sync + Send,
173        SquareGrid<'a, S>: TakeMoveNeighbors<usize, S::MoveNeighbors>,
174        SquareGrid<'a, S>: GetNeighbors<'a, usize, S::Neighbors>,
175    {
176        let mut events_loop = glutin::EventsLoop::new();
177        let window = glutin::WindowBuilder::new().with_dimensions(glutin::dpi::LogicalSize::new(
178            f64::from(self.scale) * grid.get_width() as f64,
179            f64::from(self.scale) * grid.get_height() as f64,
180        ));
181        let context = glutin::ContextBuilder::new().with_vsync(true);
182        let display = glium::Display::new(window, context, &events_loop).unwrap();
183        let renderer = Renderer::new(&display);
184
185        loop {
186            use glium::Surface;
187            if grid
188                .cycle_multi(
189                    &mut in_right,
190                    &mut in_up_right,
191                    &mut in_up,
192                    &mut in_up_left,
193                    &mut in_left,
194                    &mut in_down_left,
195                    &mut in_down,
196                    &mut in_down_right,
197                    &mut out_right,
198                    &mut out_up_right,
199                    &mut out_up,
200                    &mut out_up_left,
201                    &mut out_left,
202                    &mut out_down_left,
203                    &mut out_down,
204                    &mut out_down_right,
205                )
206                .is_err()
207            {
208                return;
209            }
210
211            let mut target = display.draw();
212            target.clear_color(0.0, 0.0, 0.0, 1.0);
213            renderer
214                .render(
215                    &display,
216                    &mut target,
217                    &grid,
218                    Default::default(),
219                    &*self.coloration,
220                    &*self.filter,
221                )
222                .unwrap();
223            target.finish().unwrap();
224
225            let mut finish = false;
226
227            // the main loop
228            events_loop.poll_events(|event| {
229                if let glutin::Event::WindowEvent {
230                    event: WindowEvent::CloseRequested,
231                    ..
232                } = event
233                {
234                    // Break from the main loop when the window is closed.
235                    finish = true;
236                }
237            });
238
239            if finish {
240                return;
241            }
242        }
243    }
244}