rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 A simple interactive mandelbrot fractal explorer
|
nuclear@0
|
3 Copyright (C) John Tsiombikas <nuclear@member.fsf.org>
|
nuclear@0
|
4
|
nuclear@0
|
5 This program is free software: you can redistribute it and/or modify
|
nuclear@0
|
6 it under the terms of the GNU General Public License as published by
|
nuclear@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
nuclear@0
|
8 (at your option) any later version.
|
nuclear@0
|
9
|
nuclear@0
|
10 This program is distributed in the hope that it will be useful,
|
nuclear@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nuclear@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nuclear@0
|
13 GNU General Public License for more details.
|
nuclear@0
|
14
|
nuclear@0
|
15 You should have received a copy of the GNU General Public License
|
nuclear@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
nuclear@0
|
17 */
|
nuclear@0
|
18 #include <stdio.h>
|
nuclear@0
|
19 #include <stdlib.h>
|
nuclear@0
|
20 #include <SDL.h>
|
nuclear@0
|
21 #include "mbrot.h"
|
nuclear@0
|
22 #include "palette.h"
|
nuclear@0
|
23
|
nuclear@0
|
24 static struct {
|
nuclear@0
|
25 int width, height;
|
nuclear@0
|
26 int num_threads;
|
nuclear@0
|
27 int num_iter;
|
nuclear@0
|
28 const char *palfile;
|
nuclear@0
|
29 } opt = { 800, 600, 1, 100, "default.pal" };
|
nuclear@0
|
30
|
nuclear@0
|
31 void init_area(void);
|
nuclear@0
|
32 void redraw(void);
|
nuclear@0
|
33 int parse_args(int argc, char **argv);
|
nuclear@0
|
34 int handle_event(SDL_Event *ev);
|
nuclear@0
|
35 int handle_keypress(int key);
|
nuclear@0
|
36
|
nuclear@0
|
37 static int must_redraw;
|
nuclear@0
|
38
|
nuclear@0
|
39 static SDL_Surface *framebuffer_surf;
|
nuclear@0
|
40
|
nuclear@0
|
41 static struct area area;
|
nuclear@0
|
42 static double aspect;
|
nuclear@0
|
43
|
nuclear@0
|
44
|
nuclear@0
|
45 int main(int argc, char **argv)
|
nuclear@0
|
46 {
|
nuclear@0
|
47 if(parse_args(argc, argv) == -1) {
|
nuclear@0
|
48 return 1;
|
nuclear@0
|
49 }
|
nuclear@0
|
50
|
nuclear@0
|
51 /* load palette file */
|
nuclear@0
|
52 if(load_palette(opt.palfile) == -1) {
|
nuclear@0
|
53 return 1;
|
nuclear@0
|
54 }
|
nuclear@0
|
55
|
nuclear@0
|
56 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
nuclear@0
|
57 if(!(framebuffer_surf = SDL_SetVideoMode(opt.width, opt.height, 32, SDL_SWSURFACE))) {
|
nuclear@0
|
58 fprintf(stderr, "failed to initialize graphics\n");
|
nuclear@0
|
59 return 1;
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 init_area();
|
nuclear@0
|
63 redraw();
|
nuclear@0
|
64
|
nuclear@0
|
65 for(;;) {
|
nuclear@0
|
66 SDL_Event ev;
|
nuclear@0
|
67 SDL_WaitEvent(&ev);
|
nuclear@0
|
68
|
nuclear@0
|
69 do {
|
nuclear@0
|
70 if(!handle_event(&ev)) {
|
nuclear@0
|
71 goto done;
|
nuclear@0
|
72 }
|
nuclear@0
|
73 } while(SDL_PollEvent(&ev));
|
nuclear@0
|
74
|
nuclear@0
|
75 if(must_redraw) {
|
nuclear@0
|
76 redraw();
|
nuclear@0
|
77 }
|
nuclear@0
|
78 }
|
nuclear@0
|
79
|
nuclear@0
|
80 done:
|
nuclear@0
|
81 SDL_Quit();
|
nuclear@0
|
82 return 0;
|
nuclear@0
|
83 }
|
nuclear@0
|
84
|
nuclear@0
|
85 void init_area(void)
|
nuclear@0
|
86 {
|
nuclear@0
|
87 aspect = (double)opt.width / (double)opt.height;
|
nuclear@0
|
88
|
nuclear@0
|
89 area.x = -2.0;
|
nuclear@0
|
90 area.width = 3.0;
|
nuclear@0
|
91 area.height = area.width / aspect;
|
nuclear@0
|
92 area.y = -area.height / 2.0;
|
nuclear@0
|
93 }
|
nuclear@0
|
94
|
nuclear@0
|
95 void redraw(void)
|
nuclear@0
|
96 {
|
nuclear@0
|
97 void *fb;
|
nuclear@0
|
98
|
nuclear@0
|
99 if(SDL_MUSTLOCK(framebuffer_surf)) {
|
nuclear@0
|
100 SDL_LockSurface(framebuffer_surf);
|
nuclear@0
|
101 }
|
nuclear@0
|
102
|
nuclear@0
|
103 fb = framebuffer_surf->pixels;
|
nuclear@0
|
104 draw_mandelbrot(fb, opt.width, opt.height, &area, opt.num_iter);
|
nuclear@0
|
105
|
nuclear@0
|
106 if(SDL_MUSTLOCK(framebuffer_surf)) {
|
nuclear@0
|
107 SDL_UnlockSurface(framebuffer_surf);
|
nuclear@0
|
108 }
|
nuclear@0
|
109
|
nuclear@0
|
110 SDL_UpdateRect(framebuffer_surf, 0, 0, opt.width, opt.height);
|
nuclear@0
|
111
|
nuclear@0
|
112 must_redraw = 0;
|
nuclear@0
|
113 }
|
nuclear@0
|
114
|
nuclear@0
|
115 int parse_args(int argc, char **argv)
|
nuclear@0
|
116 {
|
nuclear@0
|
117 int i;
|
nuclear@0
|
118 char *endp;
|
nuclear@0
|
119
|
nuclear@0
|
120 for(i=1; i<argc; i++) {
|
nuclear@0
|
121 if(argv[i][0] == '-' && argv[i][2] == 0) {
|
nuclear@0
|
122 switch(argv[i][1]) {
|
nuclear@0
|
123 case 's':
|
nuclear@0
|
124 if(sscanf(argv[++i], "%dx%d", &opt.width, &opt.height) != 2) {
|
nuclear@0
|
125 fprintf(stderr, "-s must be followed by WIDTHxHEIGHT\n");
|
nuclear@0
|
126 return -1;
|
nuclear@0
|
127 }
|
nuclear@0
|
128 break;
|
nuclear@0
|
129
|
nuclear@0
|
130 case 'i':
|
nuclear@0
|
131 opt.num_iter = strtol(argv[++i], &endp, 10);
|
nuclear@0
|
132 if(endp == argv[i]) {
|
nuclear@0
|
133 fprintf(stderr, "-i must be followed by the number of iterations\n");
|
nuclear@0
|
134 return -1;
|
nuclear@0
|
135 }
|
nuclear@0
|
136 break;
|
nuclear@0
|
137
|
nuclear@0
|
138 case 't':
|
nuclear@0
|
139 opt.num_threads = strtol(argv[++i], &endp, 10);
|
nuclear@0
|
140 if(endp == argv[i]) {
|
nuclear@0
|
141 fprintf(stderr, "-t must be followed by the number of threads\n");
|
nuclear@0
|
142 return -1;
|
nuclear@0
|
143 }
|
nuclear@0
|
144 break;
|
nuclear@0
|
145
|
nuclear@0
|
146 case 'p':
|
nuclear@0
|
147 opt.palfile = argv[++i];
|
nuclear@0
|
148 break;
|
nuclear@0
|
149
|
nuclear@0
|
150 case 'h':
|
nuclear@0
|
151 printf("usage: %s [-s <WxH>] [-i <iterations>] [-t <threads>] [-p <palette>]\n", argv[0]);
|
nuclear@0
|
152 printf(" -s <WxH> set the render size (default: %dx%d)\n", opt.width, opt.height);
|
nuclear@0
|
153 printf(" -i <iterations> set the maximum number of mandelbrot iterations (default: %d)\n", opt.num_iter);
|
nuclear@0
|
154 printf(" -t <threads> number of threads for mandelbrot calculations (default: %d)\n", opt.num_threads);
|
nuclear@0
|
155 printf(" -p <palette> use this palette file (see README) (default: %s)\n", opt.palfile);
|
nuclear@0
|
156 exit(0);
|
nuclear@0
|
157
|
nuclear@0
|
158 default:
|
nuclear@0
|
159 fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
|
nuclear@0
|
160 return -1;
|
nuclear@0
|
161 }
|
nuclear@0
|
162 } else {
|
nuclear@0
|
163 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@0
|
164 return -1;
|
nuclear@0
|
165 }
|
nuclear@0
|
166 }
|
nuclear@0
|
167 return 0;
|
nuclear@0
|
168 }
|
nuclear@0
|
169
|
nuclear@0
|
170 int handle_event(SDL_Event *ev)
|
nuclear@0
|
171 {
|
nuclear@0
|
172 static int prev_x, prev_y;
|
nuclear@0
|
173
|
nuclear@0
|
174 switch(ev->type) {
|
nuclear@0
|
175 case SDL_KEYDOWN:
|
nuclear@0
|
176 if(!handle_keypress(ev->key.keysym.sym)) {
|
nuclear@0
|
177 return 0;
|
nuclear@0
|
178 }
|
nuclear@0
|
179 break;
|
nuclear@0
|
180
|
nuclear@0
|
181 case SDL_MOUSEBUTTONDOWN:
|
nuclear@0
|
182 prev_x = ev->button.x;
|
nuclear@0
|
183 prev_y = ev->button.y;
|
nuclear@0
|
184 break;
|
nuclear@0
|
185
|
nuclear@0
|
186 case SDL_MOUSEMOTION:
|
nuclear@0
|
187 {
|
nuclear@0
|
188 int dx = ev->motion.x - prev_x;
|
nuclear@0
|
189 int dy = ev->motion.y - prev_y;
|
nuclear@0
|
190 prev_x = ev->motion.x;
|
nuclear@0
|
191 prev_y = ev->motion.y;
|
nuclear@0
|
192
|
nuclear@0
|
193 if(ev->motion.state & 1) {
|
nuclear@0
|
194 /* pan */
|
nuclear@0
|
195 area.x -= area.width * (double)dx / (double)opt.width;
|
nuclear@0
|
196 area.y -= area.height * (double)dy / (double)opt.height;
|
nuclear@0
|
197 must_redraw = 1;
|
nuclear@0
|
198 } else if(ev->motion.state & 4) {
|
nuclear@0
|
199 /* zoom */
|
nuclear@0
|
200 double zoom = 1.0 + (double)dy / (double)opt.height;
|
nuclear@0
|
201 double new_width = area.width * zoom;
|
nuclear@0
|
202 double new_height = area.height * zoom;
|
nuclear@0
|
203 area.x += (area.width - new_width) / 2.0;
|
nuclear@0
|
204 area.y += (area.height - new_height) / 2.0;
|
nuclear@0
|
205 area.width = new_width;
|
nuclear@0
|
206 area.height = new_height;
|
nuclear@0
|
207 must_redraw = 1;
|
nuclear@0
|
208 }
|
nuclear@0
|
209 }
|
nuclear@0
|
210 break;
|
nuclear@0
|
211
|
nuclear@0
|
212 case SDL_VIDEOEXPOSE:
|
nuclear@0
|
213 must_redraw = 1;
|
nuclear@0
|
214 break;
|
nuclear@0
|
215
|
nuclear@0
|
216 case SDL_QUIT:
|
nuclear@0
|
217 return 0;
|
nuclear@0
|
218 }
|
nuclear@0
|
219 return 1;
|
nuclear@0
|
220 }
|
nuclear@0
|
221
|
nuclear@0
|
222 int handle_keypress(int key)
|
nuclear@0
|
223 {
|
nuclear@0
|
224 switch(key) {
|
nuclear@0
|
225 case SDLK_ESCAPE:
|
nuclear@0
|
226 case 'q':
|
nuclear@0
|
227 case 'Q':
|
nuclear@0
|
228 return 0;
|
nuclear@0
|
229
|
nuclear@0
|
230 case '-':
|
nuclear@0
|
231 if(opt.num_iter > 10) {
|
nuclear@0
|
232 opt.num_iter -= 10;
|
nuclear@0
|
233 printf("iterations: %d\n", opt.num_iter);
|
nuclear@0
|
234 must_redraw = 1;
|
nuclear@0
|
235 }
|
nuclear@0
|
236 break;
|
nuclear@0
|
237
|
nuclear@0
|
238 case '=':
|
nuclear@0
|
239 opt.num_iter += 10;
|
nuclear@0
|
240 printf("iterations: %d\n", opt.num_iter);
|
nuclear@0
|
241 must_redraw = 1;
|
nuclear@0
|
242 break;
|
nuclear@0
|
243
|
nuclear@0
|
244 case '?':
|
nuclear@0
|
245 case '/':
|
nuclear@0
|
246 printf("current coordinates: (%f, %f) -> (%f, %f)\n", area.x, area.y,
|
nuclear@0
|
247 area.x + area.width, area.y + area.height);
|
nuclear@0
|
248 break;
|
nuclear@0
|
249
|
nuclear@0
|
250 case '\b':
|
nuclear@0
|
251 init_area();
|
nuclear@0
|
252 must_redraw = 1;
|
nuclear@0
|
253 break;
|
nuclear@0
|
254
|
nuclear@0
|
255 default:
|
nuclear@0
|
256 break;
|
nuclear@0
|
257 }
|
nuclear@0
|
258 return 1;
|
nuclear@0
|
259 }
|