• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2010 Kristian Høgsberg
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <math.h>
32 #include <errno.h>
33 #include <cairo.h>
34 
35 #include <wayland-client.h>
36 #include "window.h"
37 
38 struct smoke {
39 	struct display *display;
40 	struct window *window;
41 	struct widget *widget;
42 	int width, height;
43 	int current;
44 	struct { float *d, *u, *v; } b[2];
45 };
46 
diffuse(struct smoke * smoke,uint32_t time,float * source,float * dest)47 static void diffuse(struct smoke *smoke, uint32_t time,
48 		    float *source, float *dest)
49 {
50 	float *s, *d;
51 	int x, y, k, stride;
52 	float t, a = 0.0002;
53 
54 	stride = smoke->width;
55 
56 	for (k = 0; k < 5; k++) {
57 		for (y = 1; y < smoke->height - 1; y++) {
58 			s = source + y * stride;
59 			d = dest + y * stride;
60 			for (x = 1; x < smoke->width - 1; x++) {
61 				t = d[x - 1] + d[x + 1] +
62 					d[x - stride] + d[x + stride];
63 				d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
64 			}
65 		}
66 	}
67 }
68 
advect(struct smoke * smoke,uint32_t time,float * uu,float * vv,float * source,float * dest)69 static void advect(struct smoke *smoke, uint32_t time,
70 		   float *uu, float *vv, float *source, float *dest)
71 {
72 	float *s, *d;
73 	float *u, *v;
74 	int x, y, stride;
75 	int i, j;
76 	float px, py, fx, fy;
77 
78 	stride = smoke->width;
79 
80 	for (y = 1; y < smoke->height - 1; y++) {
81 		d = dest + y * stride;
82 		u = uu + y * stride;
83 		v = vv + y * stride;
84 
85 		for (x = 1; x < smoke->width - 1; x++) {
86 			px = x - u[x];
87 			py = y - v[x];
88 			if (px < 0.5)
89 				px = 0.5;
90 			if (py < 0.5)
91 				py = 0.5;
92 			if (px > smoke->width - 1.5)
93 				px = smoke->width - 1.5;
94 			if (py > smoke->height - 1.5)
95 				py = smoke->height - 1.5;
96 			i = (int) px;
97 			j = (int) py;
98 			fx = px - i;
99 			fy = py - j;
100 			s = source + j * stride + i;
101 			d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
102 				(s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
103 		}
104 	}
105 }
106 
project(struct smoke * smoke,uint32_t time,float * u,float * v,float * p,float * div)107 static void project(struct smoke *smoke, uint32_t time,
108 		    float *u, float *v, float *p, float *div)
109 {
110 	int x, y, k, l, s;
111 	float h;
112 
113 	h = 1.0 / smoke->width;
114 	s = smoke->width;
115 	memset(p, 0, smoke->height * smoke->width);
116 	for (y = 1; y < smoke->height - 1; y++) {
117 		l = y * s;
118 		for (x = 1; x < smoke->width - 1; x++) {
119 			div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
120 						 v[l + x + s] - v[l + x - s]);
121 			p[l + x] = 0;
122 		}
123 	}
124 
125 	for (k = 0; k < 5; k++) {
126 		for (y = 1; y < smoke->height - 1; y++) {
127 			l = y * s;
128 			for (x = 1; x < smoke->width - 1; x++) {
129 				p[l + x] = (div[l + x] +
130 					    p[l + x - 1] +
131 					    p[l + x + 1] +
132 					    p[l + x - s] +
133 					    p[l + x + s]) / 4;
134 			}
135 		}
136 	}
137 
138 	for (y = 1; y < smoke->height - 1; y++) {
139 		l = y * s;
140 		for (x = 1; x < smoke->width - 1; x++) {
141 			u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
142 			v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
143 		}
144 	}
145 }
146 
render(struct smoke * smoke,cairo_surface_t * surface)147 static void render(struct smoke *smoke, cairo_surface_t *surface)
148 {
149 	unsigned char *dest;
150 	int x, y, width, height, stride;
151 	float *s;
152 	uint32_t *d, c, a;
153 
154 	dest = cairo_image_surface_get_data(surface);
155 	width = cairo_image_surface_get_width(surface);
156 	height = cairo_image_surface_get_height(surface);
157 	stride = cairo_image_surface_get_stride(surface);
158 
159 	for (y = 1; y < height - 1; y++) {
160 		s = smoke->b[smoke->current].d + y * smoke->height;
161 		d = (uint32_t *) (dest + y * stride);
162 		for (x = 1; x < width - 1; x++) {
163 			c = (int) (s[x] * 800);
164 			if (c > 255)
165 				c = 255;
166 			a = c;
167 			if (a < 0x33)
168 				a = 0x33;
169 			d[x] = (a << 24) | (c << 16) | (c << 8) | c;
170 		}
171 	}
172 }
173 
174 static void
redraw_handler(struct widget * widget,void * data)175 redraw_handler(struct widget *widget, void *data)
176 {
177 	struct smoke *smoke = data;
178 	uint32_t time = widget_get_last_time(smoke->widget);
179 	cairo_surface_t *surface;
180 
181 	diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
182 	diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
183 	project(smoke, time / 30,
184 		smoke->b[1].u, smoke->b[1].v,
185 		smoke->b[0].u, smoke->b[0].v);
186 	advect(smoke, time / 30,
187 	       smoke->b[1].u, smoke->b[1].v,
188 	       smoke->b[1].u, smoke->b[0].u);
189 	advect(smoke, time / 30,
190 	       smoke->b[1].u, smoke->b[1].v,
191 	       smoke->b[1].v, smoke->b[0].v);
192 	project(smoke, time / 30,
193 		smoke->b[0].u, smoke->b[0].v,
194 		smoke->b[1].u, smoke->b[1].v);
195 
196 	diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
197 	advect(smoke, time / 30,
198 	       smoke->b[0].u, smoke->b[0].v,
199 	       smoke->b[1].d, smoke->b[0].d);
200 
201 	surface = window_get_surface(smoke->window);
202 
203 	render(smoke, surface);
204 
205 	cairo_surface_destroy(surface);
206 
207 	widget_schedule_redraw(smoke->widget);
208 }
209 
210 static void
smoke_motion_handler(struct smoke * smoke,float x,float y)211 smoke_motion_handler(struct smoke *smoke, float x, float y)
212 {
213 	int i, i0, i1, j, j0, j1, k, d = 5;
214 
215 	if (x - d < 1)
216 		i0 = 1;
217 	else
218 		i0 = x - d;
219 	if (i0 + 2 * d > smoke->width - 1)
220 		i1 = smoke->width - 1;
221 	else
222 		i1 = i0 + 2 * d;
223 
224 	if (y - d < 1)
225 		j0 = 1;
226 	else
227 		j0 = y - d;
228 	if (j0 + 2 * d > smoke->height - 1)
229 		j1 = smoke->height - 1;
230 	else
231 		j1 = j0 + 2 * d;
232 
233 	for (i = i0; i < i1; i++)
234 		for (j = j0; j < j1; j++) {
235 			k = j * smoke->width + i;
236 			smoke->b[0].u[k] += 256 - (random() & 512);
237 			smoke->b[0].v[k] += 256 - (random() & 512);
238 			smoke->b[0].d[k] += 1;
239 		}
240 }
241 
242 static int
mouse_motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)243 mouse_motion_handler(struct widget *widget, struct input *input,
244 		     uint32_t time, float x, float y, void *data)
245 {
246 	smoke_motion_handler(data, x, y);
247 
248 	return CURSOR_HAND1;
249 }
250 
251 static void
touch_motion_handler(struct widget * widget,struct input * input,uint32_t time,int32_t id,float x,float y,void * data)252 touch_motion_handler(struct widget *widget, struct input *input,
253 		     uint32_t time, int32_t id, float x, float y, void *data)
254 {
255 	smoke_motion_handler(data, x, y);
256 }
257 
258 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)259 resize_handler(struct widget *widget,
260 	       int32_t width, int32_t height, void *data)
261 {
262 	struct smoke *smoke = data;
263 
264 	/* Don't resize me */
265 	widget_set_size(smoke->widget, smoke->width, smoke->height);
266 }
267 
main(int argc,char * argv[])268 int main(int argc, char *argv[])
269 {
270 	struct timespec ts;
271 	struct smoke smoke;
272 	struct display *d;
273 	int size;
274 
275 	d = display_create(&argc, argv);
276 	if (d == NULL) {
277 		fprintf(stderr, "failed to create display: %s\n",
278 			strerror(errno));
279 		return -1;
280 	}
281 
282 	smoke.width = 200;
283 	smoke.height = 200;
284 	smoke.display = d;
285 	smoke.window = window_create(d);
286 	smoke.widget = window_add_widget(smoke.window, &smoke);
287 	window_set_title(smoke.window, "smoke");
288 
289 	window_set_buffer_type(smoke.window, WINDOW_BUFFER_TYPE_SHM);
290 	clock_gettime(CLOCK_MONOTONIC, &ts);
291 	srandom(ts.tv_nsec);
292 
293 	smoke.current = 0;
294 	size = smoke.height * smoke.width;
295 	smoke.b[0].d = calloc(size, sizeof(float));
296 	smoke.b[0].u = calloc(size, sizeof(float));
297 	smoke.b[0].v = calloc(size, sizeof(float));
298 	smoke.b[1].d = calloc(size, sizeof(float));
299 	smoke.b[1].u = calloc(size, sizeof(float));
300 	smoke.b[1].v = calloc(size, sizeof(float));
301 
302 	widget_set_motion_handler(smoke.widget, mouse_motion_handler);
303 	widget_set_touch_motion_handler(smoke.widget, touch_motion_handler);
304 	widget_set_resize_handler(smoke.widget, resize_handler);
305 	widget_set_redraw_handler(smoke.widget, redraw_handler);
306 
307 	window_set_user_data(smoke.window, &smoke);
308 
309 	widget_schedule_resize(smoke.widget, smoke.width, smoke.height);
310 
311 	display_run(d);
312 
313 	widget_destroy(smoke.widget);
314 	window_destroy(smoke.window);
315 	display_destroy(d);
316 
317 	return 0;
318 }
319