1
- use num:: { Complex } ;
2
- use std:: str:: FromStr ;
1
+ use num:: Complex ;
3
2
use std:: env;
3
+ use std:: str:: FromStr ;
4
4
5
5
use crossbeam;
6
6
7
- use image:: ColorType ;
8
7
use image:: png:: PngEncoder ;
8
+ use image:: ColorType ;
9
9
use std:: fs:: File ;
10
10
11
- fn main ( ) {
12
- let args: Vec < String > = env:: args ( ) . collect ( ) ;
13
- if args. len ( ) != 6 {
14
- eprintln ! ( "Usage: {} FILE PIXELS SEPARATOR UPPERLEFT LOWERRIGHT" , args[ 0 ] ) ;
15
- eprintln ! ( "Example: {} mandel.png 1000x750 -1.20,0.35 -1,0.20" , args[ 0 ] ) ;
16
- std:: process:: exit ( 1 ) ;
17
- }
18
- let bounds= parse_pair ( & args[ 2 ] , char:: from_str ( & args[ 3 ] ) . expect ( "Seperator conversion failed" ) ) . expect ( "Parsing of image dimensions failed with given arguments" ) ;
19
- let upper_left = parse_complex ( & args[ 4 ] ) . expect ( "Parsing of upper left complex number failed" ) ;
20
- let lower_right = parse_complex ( & args[ 5 ] ) . expect ( "Parsing of lower right complex number failed" ) ;
11
+ use log:: { debug, error} ;
21
12
22
- let mut pixels = vec ! [ 0 ; bounds. 0 * bounds. 1 ] ;
23
-
13
+ use pixels:: { Error , Pixels , SurfaceTexture } ;
14
+ use winit:: dpi:: { LogicalPosition , LogicalSize , PhysicalSize } ;
15
+ use winit:: event:: { Event , VirtualKeyCode } ;
16
+ use winit:: event_loop:: { ControlFlow , EventLoop } ;
17
+ use winit_input_helper:: WinitInputHelper ;
24
18
25
- // Multithreading stuff here
26
- let threads = 16 ; // Higher number = More speed
27
- let rows_per_band = bounds. 1 / threads + 1 ;
19
+ const SCREEN_WIDTH : usize = 1000 ;
20
+ const SCREEN_HEIGHT : usize = 1000 ;
21
+
22
+ fn main ( ) -> Result < ( ) , Error > {
23
+ // Argument parsing here. (Custom window size) TODO: Better argument handling
24
+ // let args: Vec<String> = env::args().collect();
25
+ // if args.len() != 6 {
26
+ // eprintln!("Usage: {} FILE PIXELS SEPARATOR UPPERLEFT LOWERRIGHT", args[0]);
27
+ // eprintln!("Example: {} mandel.png 1000x750 x -1.20,0.35 -1,0.20", args[0]);
28
+ // std::process::exit(1);
29
+ // }
30
+
31
+ // let bounds= parse_pair(&args[2], char::from_str(&args[3]).expect("Seperator conversion failed")).expect("Parsing of image dimensions failed with given arguments");
32
+ // let upper_left = parse_complex(&args[4]).expect("Parsing of upper left complex number failed");
33
+ // let lower_right = parse_complex(&args[5]).expect("Parsing of lower right complex number failed");
34
+
35
+ let event_loop = EventLoop :: new ( ) ;
36
+ let mut input = WinitInputHelper :: new ( ) ;
37
+ let window = create_window ( "Mandelbrot Explorer" , & event_loop) ;
38
+
39
+ let bounds = ( SCREEN_WIDTH , SCREEN_HEIGHT ) ;
40
+ let mut upper_left = Complex { re : -1.0 , im : 1.0 } ;
41
+ let mut lower_right = Complex { re : 1.0 , im : -1.0 } ;
42
+
43
+ //let mut pixels = vec![0; bounds.0 * bounds.1];
28
44
29
- {
30
- let bands: Vec < & mut [ u8 ] > = pixels. chunks_mut ( rows_per_band * bounds. 0 ) . collect ( ) ;
31
- crossbeam:: scope ( |spawner| {
32
- for ( i, band) in bands. into_iter ( ) . enumerate ( ) {
33
- let top = rows_per_band * i;
34
- let height = band. len ( ) / bounds. 0 ;
35
- let band_bounds = ( bounds. 0 , height) ;
36
- let band_upper_left = pixel_to_point ( bounds, ( 0 , top) , upper_left, lower_right) ;
37
- let band_lower_right = pixel_to_point ( bounds, ( bounds. 0 , top + height) , upper_left, lower_right) ;
45
+ let surface_texture = SurfaceTexture :: new ( SCREEN_WIDTH as u32 , SCREEN_HEIGHT as u32 , & window) ;
46
+ let mut pixels = Pixels :: new ( SCREEN_WIDTH as u32 , SCREEN_HEIGHT as u32 , surface_texture)
47
+ . expect ( "Pixels failed to initialize" ) ;
48
+
49
+ println ! ( "{}" , pixels. get_frame( ) . len( ) ) ; // Make sure bounds correctly define amount of pixels
50
+
51
+ render_multi ( pixels. get_frame ( ) , bounds, upper_left, lower_right) ;
52
+
53
+ event_loop. run ( move |event, _, control_flow| {
54
+ // The one and only event that winit_input_helper doesn't have for us...
55
+ if let Event :: RedrawRequested ( _) = event {
56
+ render_multi ( pixels. get_frame ( ) , bounds, upper_left, lower_right) ;
57
+ if pixels
58
+ . render ( )
59
+ . map_err ( |e| error ! ( "pixels.render() failed: {}" , e) ) // I probably broke this :P
60
+ . is_err ( )
61
+ {
62
+ * control_flow = ControlFlow :: Exit ;
63
+ return ;
64
+ }
65
+ }
66
+
67
+ // For everything else, for let winit_input_helper collect events to build its state.
68
+ // It returns `true` when it is time to update our game state and request a redraw.
69
+ if input. update ( & event) {
70
+ // Close events
71
+ if input. key_pressed ( VirtualKeyCode :: Escape ) || input. quit ( ) {
72
+ * control_flow = ControlFlow :: Exit ;
73
+ return ;
74
+ }
75
+ if input. key_pressed ( VirtualKeyCode :: W ) {
76
+ let displacement = Complex {
77
+ re : 0.0 * ( upper_left. re - lower_right. re ) ,
78
+ im : 0.05 * ( upper_left. im - lower_right. im ) ,
79
+ } ;
80
+
81
+ upper_left += displacement;
82
+ lower_right += displacement;
83
+ window. request_redraw ( ) ;
84
+ }
85
+ if input. key_pressed ( VirtualKeyCode :: A ) {
86
+ let displacement = Complex {
87
+ re : 0.05 * ( upper_left. re - lower_right. re ) ,
88
+ im : 0.0 * ( upper_left. im - lower_right. im ) ,
89
+ } ;
90
+
91
+ upper_left += displacement;
92
+ lower_right += displacement;
93
+ window. request_redraw ( ) ;
94
+ }
95
+ if input. key_pressed ( VirtualKeyCode :: S ) {
96
+ let displacement = Complex {
97
+ re : 0.0 * ( upper_left. re - lower_right. re ) ,
98
+ im : -0.05 * ( upper_left. im - lower_right. im ) ,
99
+ } ;
100
+
101
+ upper_left += displacement;
102
+ lower_right += displacement;
103
+ window. request_redraw ( ) ;
104
+ }
105
+ if input. key_pressed ( VirtualKeyCode :: D ) {
106
+ let displacement = Complex {
107
+ re : -0.05 * ( upper_left. re - lower_right. re ) ,
108
+ im : 0.0 * ( upper_left. im - lower_right. im ) ,
109
+ } ;
38
110
39
- spawner . spawn ( move |_| {
40
- render ( band , band_bounds , band_upper_left , band_lower_right ) ;
41
- } ) ;
111
+ upper_left += displacement ;
112
+ lower_right += displacement ;
113
+ window . request_redraw ( ) ;
42
114
}
43
- } ) . unwrap ( ) ;
44
- }
45
115
46
- write_image ( & args[ 1 ] , & pixels, bounds) . expect ( "Image writing failed" ) ;
116
+ // Zooming TODO: Still not completely centered (for small bounds)
117
+ if input. key_pressed ( VirtualKeyCode :: Z ) {
118
+ let scalar = 0.10 ;
119
+
120
+ upper_left -= scalar* ( upper_left-lower_right) /2.0 ;
121
+ lower_right += scalar* ( upper_left-lower_right) /2.0 ;
122
+ window. request_redraw ( ) ;
123
+ }
124
+ if input. key_pressed ( VirtualKeyCode :: X ) {
125
+ let scalar = 0.10 ;
126
+
127
+ upper_left += scalar* ( upper_left-lower_right) /2.0 ;
128
+ lower_right -= scalar* ( upper_left-lower_right) /2.0 ;
129
+ window. request_redraw ( ) ;
130
+ }
131
+
132
+ // Resetting
133
+ if input. key_pressed ( VirtualKeyCode :: Space ) {
134
+ upper_left = Complex { re : -1.0 , im : 1.0 } ;
135
+ lower_right = Complex { re : 1.0 , im : -1.0 } ;
136
+ window. request_redraw ( ) ;
137
+ }
138
+ }
139
+ } ) ;
140
+ //write_image(&args[1], &pixels, bounds).expect("Image writing failed"); TODO: Use somewhere else
47
141
}
48
142
49
- // Write pixel buffer to file
50
- fn write_image ( filename : & str , pixels : & [ u8 ] , bounds : ( usize , usize ) ) -> Result < ( ) , std:: io:: Error > {
51
- let output = File :: create ( filename) ?;
143
+ // Multithreaded render
144
+ fn render_multi (
145
+ pixels : & mut [ u8 ] ,
146
+ bounds : ( usize , usize ) ,
147
+ upper_left : Complex < f64 > ,
148
+ lower_right : Complex < f64 > ,
149
+ ) {
150
+ println ! (
151
+ "Rendering between {},{} and {},{}" ,
152
+ upper_left. re, upper_left. im, lower_right. re, lower_right. im
153
+ ) ;
154
+
155
+ // Multithreading stuff here
156
+ let threads = 16 ; // Higher number = More speed
157
+ let rows_per_band = bounds. 1 / threads + 1 ;
158
+
159
+ let bands: Vec < & mut [ u8 ] > = pixels. chunks_mut ( rows_per_band * bounds. 0 * 4 ) . collect ( ) ;
160
+ crossbeam:: scope ( |spawner| {
161
+ for ( i, band) in bands. into_iter ( ) . enumerate ( ) {
162
+ let top = rows_per_band * i;
163
+ let height = band. len ( ) / 4 / bounds. 0 ;
164
+ let band_bounds = ( bounds. 0 , height) ;
165
+ let band_upper_left = pixel_to_point ( bounds, ( 0 , top) , upper_left, lower_right) ;
166
+ let band_lower_right =
167
+ pixel_to_point ( bounds, ( bounds. 0 , top + height) , upper_left, lower_right) ;
52
168
53
- let encoder = PngEncoder :: new ( output ) ;
54
- match encoder . encode ( & pixels , bounds . 0 as u32 , bounds . 1 as u32 , ColorType :: L8 ) {
55
- Ok ( ( ) ) => ( ) ,
56
- Err ( e ) => return Err ( std :: io :: Error :: new ( std :: io :: ErrorKind :: Other , e . to_string ( ) ) ) // This seems scuffed. TODO: Figure out a better way
57
- } ; // L8 is 8 bit luminence
58
- Ok ( ( ) ) // Can error out through the two ? but otherwise return OK(()) -- Ok with a unit
169
+ spawner . spawn ( move |_| {
170
+ render ( band , band_bounds , band_upper_left , band_lower_right ) ;
171
+ } ) ;
172
+ }
173
+ } )
174
+ . unwrap ( ) ;
59
175
}
60
176
61
177
// Render a portion of the mandelbrot set into a given buffer of pixels
62
- fn render ( pixels : & mut [ u8 ] , bounds : ( usize , usize ) , upper_left : Complex < f64 > , lower_right : Complex < f64 > ) {
63
- assert ! ( pixels. len( ) == bounds. 0 * bounds. 1 ) ; // Make sure bounds correctly define amount of pixels
178
+ fn render (
179
+ pixels : & mut [ u8 ] ,
180
+ bounds : ( usize , usize ) ,
181
+ upper_left : Complex < f64 > ,
182
+ lower_right : Complex < f64 > ,
183
+ ) {
184
+ assert ! ( pixels. len( ) == bounds. 0 * bounds. 1 * 4 ) ; // Make sure bounds correctly define amount of pixels
64
185
65
186
for row in 0 ..bounds. 1 {
66
187
for column in 0 ..bounds. 0 {
67
188
let point = pixel_to_point ( bounds, ( column, row) , upper_left, lower_right) ;
68
- pixels [ row * bounds . 0 + column ] = match escape_time ( point, 255 ) {
189
+ let point_shade = match escape_time ( point, 255 ) {
69
190
None => 0 ,
70
- Some ( count) => 255 - count as u8
191
+ Some ( count) => 255 - count as u8 ,
71
192
} ;
72
- }
73
- }
74
- }
75
193
76
- // Parse string to coordinates
77
- fn parse_pair < T : FromStr > ( s : & str , separator : char ) -> Option < ( T , T ) > {
78
- match s. find ( separator) { // Find the separator location
79
- None => None ,
80
- Some ( index) => {
81
- match ( T :: from_str ( & s[ ..index] ) , T :: from_str ( & s[ index + 1 ..] ) ) { // Match on tuple
82
- ( Ok ( l) , Ok ( r) ) => Some ( ( l, r) ) ,
83
- _ => None
84
- }
194
+ let pixel_color = [ 0 , point_shade, point_shade, point_shade] ;
195
+
196
+ let pixel_start = ( row * bounds. 0 + column) * 4 ;
197
+
198
+ pixels[ pixel_start..pixel_start + 4 ] . copy_from_slice ( & pixel_color)
85
199
}
86
200
}
87
201
}
88
202
89
- #[ test]
90
- fn test_parse_pair ( ) {
91
- assert_eq ! ( parse_pair:: <i32 >( "" , ',' ) , None ) ;
92
- assert_eq ! ( parse_pair:: <i32 >( "10," , ',' ) , None ) ;
93
- assert_eq ! ( parse_pair:: <i32 >( ",10" , ',' ) , None ) ;
94
- assert_eq ! ( parse_pair:: <i32 >( "10,20" , ',' ) , Some ( ( 10 , 20 ) ) ) ;
95
- }
203
+ // Create the application window
204
+ fn create_window ( title : & str , event_loop : & EventLoop < ( ) > ) -> winit:: window:: Window {
205
+ // Create a hidden window so we can estimate a good default window size
206
+ let window = winit:: window:: WindowBuilder :: new ( )
207
+ . with_visible ( true )
208
+ . with_title ( title)
209
+ . with_inner_size ( LogicalSize :: new ( 1000 , 1000 ) )
210
+ . build ( event_loop)
211
+ . unwrap ( ) ;
96
212
97
- // Parse string to complex number ex: 1.03,2.58 -> Complex<f64> {re: 1.03, im: 2.58}
98
- fn parse_complex ( s : & str ) -> Option < Complex < f64 > > {
99
- match parse_pair ( s, ',' ) {
100
- Some ( ( re, im) ) => Some ( Complex { re, im } ) ,
101
- None => None
102
- }
213
+ window
103
214
}
104
215
105
216
// Function for mapping a given pixel position in a given image size to a point on the complex plane within two given complex points
106
- fn pixel_to_point ( bounds : ( usize , usize ) , pixel : ( usize , usize ) , upper_left : Complex < f64 > , lower_right : Complex < f64 > ) -> Complex < f64 > {
217
+ fn pixel_to_point (
218
+ bounds : ( usize , usize ) ,
219
+ pixel : ( usize , usize ) ,
220
+ upper_left : Complex < f64 > ,
221
+ lower_right : Complex < f64 > ,
222
+ ) -> Complex < f64 > {
107
223
// Calculate (width, height) on complex plane
108
- let ( width, height) = ( lower_right. re - upper_left. re , upper_left. im - lower_right. im ) ;
224
+ let ( width, height) = (
225
+ lower_right. re - upper_left. re ,
226
+ upper_left. im - lower_right. im ,
227
+ ) ;
109
228
110
229
Complex {
111
230
re : upper_left. re + pixel. 0 as f64 * width / bounds. 0 as f64 ,
112
- im : upper_left. im - pixel. 1 as f64 * height / bounds. 1 as f64 // Negative to flip the reversed axis in the pixel world
231
+ im : upper_left. im - pixel. 1 as f64 * height / bounds. 1 as f64 , // Negative to flip the reversed axis in the pixel world
113
232
}
114
233
}
115
234
116
235
#[ test]
117
236
fn test_pixel_to_point ( ) {
118
- assert_eq ! ( pixel_to_point( ( 100 , 200 ) , ( 25 , 175 ) , Complex { re: -1.0 , im: 1.0 } , Complex { re: 1.0 , im: -1.0 } ) , Complex { re: -0.5 , im: -0.75 } )
237
+ assert_eq ! (
238
+ pixel_to_point(
239
+ ( 100 , 200 ) ,
240
+ ( 25 , 175 ) ,
241
+ Complex { re: -1.0 , im: 1.0 } ,
242
+ Complex { re: 1.0 , im: -1.0 }
243
+ ) ,
244
+ Complex {
245
+ re: -0.5 ,
246
+ im: -0.75
247
+ }
248
+ )
119
249
}
120
250
121
251
// Function for determining the mandelbrot set escape time of a given point on the complex plane
122
252
fn escape_time ( c : Complex < f64 > , limit : usize ) -> Option < usize > {
123
- let mut z = Complex { re : 0.0 , im : 0.0 } ;
253
+ let mut z = Complex { re : 0.0 , im : 0.0 } ;
124
254
for i in 0 ..limit {
125
- if z. norm_sqr ( ) > 4.0 { // Square of the distance to the origin of the complex plane
255
+ if z. norm_sqr ( ) > 4.0 {
256
+ // Square of the distance to the origin of the complex plane
126
257
return Some ( i) ;
127
258
}
128
259
z = z * z + c;
129
260
}
130
261
131
262
None
132
- }
263
+ }
0 commit comments