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 <cairo.h>
33
34 #include <wayland-client.h>
35 #include "window.h"
36
37 struct smoke {
38 struct display *display;
39 struct window *window;
40 struct widget *widget;
41 int width, height;
42 int current;
43 struct { float *d, *u, *v; } b[2];
44 };
45
diffuse(struct smoke * smoke,uint32_t time,float * source,float * dest)46 static void diffuse(struct smoke *smoke, uint32_t time,
47 float *source, float *dest)
48 {
49 float *s, *d;
50 int x, y, k, stride;
51 float t, a = 0.0002;
52
53 stride = smoke->width;
54
55 for (k = 0; k < 5; k++) {
56 for (y = 1; y < smoke->height - 1; y++) {
57 s = source + y * stride;
58 d = dest + y * stride;
59 for (x = 1; x < smoke->width - 1; x++) {
60 t = d[x - 1] + d[x + 1] +
61 d[x - stride] + d[x + stride];
62 d[x] = (s[x] + a * t) / (1 + 4 * a) * 0.995;
63 }
64 }
65 }
66 }
67
advect(struct smoke * smoke,uint32_t time,float * uu,float * vv,float * source,float * dest)68 static void advect(struct smoke *smoke, uint32_t time,
69 float *uu, float *vv, float *source, float *dest)
70 {
71 float *s, *d;
72 float *u, *v;
73 int x, y, stride;
74 int i, j;
75 float px, py, fx, fy;
76
77 stride = smoke->width;
78
79 for (y = 1; y < smoke->height - 1; y++) {
80 d = dest + y * stride;
81 u = uu + y * stride;
82 v = vv + y * stride;
83
84 for (x = 1; x < smoke->width - 1; x++) {
85 px = x - u[x];
86 py = y - v[x];
87 if (px < 0.5)
88 px = 0.5;
89 if (py < 0.5)
90 py = 0.5;
91 if (px > smoke->width - 1.5)
92 px = smoke->width - 1.5;
93 if (py > smoke->height - 1.5)
94 py = smoke->height - 1.5;
95 i = (int) px;
96 j = (int) py;
97 fx = px - i;
98 fy = py - j;
99 s = source + j * stride + i;
100 d[x] = (s[0] * (1 - fx) + s[1] * fx) * (1 - fy) +
101 (s[stride] * (1 - fx) + s[stride + 1] * fx) * fy;
102 }
103 }
104 }
105
project(struct smoke * smoke,uint32_t time,float * u,float * v,float * p,float * div)106 static void project(struct smoke *smoke, uint32_t time,
107 float *u, float *v, float *p, float *div)
108 {
109 int x, y, k, l, s;
110 float h;
111
112 h = 1.0 / smoke->width;
113 s = smoke->width;
114 memset(p, 0, smoke->height * smoke->width);
115 for (y = 1; y < smoke->height - 1; y++) {
116 l = y * s;
117 for (x = 1; x < smoke->width - 1; x++) {
118 div[l + x] = -0.5 * h * (u[l + x + 1] - u[l + x - 1] +
119 v[l + x + s] - v[l + x - s]);
120 p[l + x] = 0;
121 }
122 }
123
124 for (k = 0; k < 5; k++) {
125 for (y = 1; y < smoke->height - 1; y++) {
126 l = y * s;
127 for (x = 1; x < smoke->width - 1; x++) {
128 p[l + x] = (div[l + x] +
129 p[l + x - 1] +
130 p[l + x + 1] +
131 p[l + x - s] +
132 p[l + x + s]) / 4;
133 }
134 }
135 }
136
137 for (y = 1; y < smoke->height - 1; y++) {
138 l = y * s;
139 for (x = 1; x < smoke->width - 1; x++) {
140 u[l + x] -= 0.5 * (p[l + x + 1] - p[l + x - 1]) / h;
141 v[l + x] -= 0.5 * (p[l + x + s] - p[l + x - s]) / h;
142 }
143 }
144 }
145
render(struct smoke * smoke,cairo_surface_t * surface)146 static void render(struct smoke *smoke, cairo_surface_t *surface)
147 {
148 unsigned char *dest;
149 int x, y, width, height, stride;
150 float *s;
151 uint32_t *d, c, a;
152
153 dest = cairo_image_surface_get_data(surface);
154 width = cairo_image_surface_get_width(surface);
155 height = cairo_image_surface_get_height(surface);
156 stride = cairo_image_surface_get_stride(surface);
157
158 for (y = 1; y < height - 1; y++) {
159 s = smoke->b[smoke->current].d + y * smoke->height;
160 d = (uint32_t *) (dest + y * stride);
161 for (x = 1; x < width - 1; x++) {
162 c = (int) (s[x] * 800);
163 if (c > 255)
164 c = 255;
165 a = c;
166 if (a < 0x33)
167 a = 0x33;
168 d[x] = (a << 24) | (c << 16) | (c << 8) | c;
169 }
170 }
171 }
172
173 static void
redraw_handler(struct widget * widget,void * data)174 redraw_handler(struct widget *widget, void *data)
175 {
176 struct smoke *smoke = data;
177 uint32_t time = widget_get_last_time(smoke->widget);
178 cairo_surface_t *surface;
179
180 diffuse(smoke, time / 30, smoke->b[0].u, smoke->b[1].u);
181 diffuse(smoke, time / 30, smoke->b[0].v, smoke->b[1].v);
182 project(smoke, time / 30,
183 smoke->b[1].u, smoke->b[1].v,
184 smoke->b[0].u, smoke->b[0].v);
185 advect(smoke, time / 30,
186 smoke->b[1].u, smoke->b[1].v,
187 smoke->b[1].u, smoke->b[0].u);
188 advect(smoke, time / 30,
189 smoke->b[1].u, smoke->b[1].v,
190 smoke->b[1].v, smoke->b[0].v);
191 project(smoke, time / 30,
192 smoke->b[0].u, smoke->b[0].v,
193 smoke->b[1].u, smoke->b[1].v);
194
195 diffuse(smoke, time / 30, smoke->b[0].d, smoke->b[1].d);
196 advect(smoke, time / 30,
197 smoke->b[0].u, smoke->b[0].v,
198 smoke->b[1].d, smoke->b[0].d);
199
200 surface = window_get_surface(smoke->window);
201
202 render(smoke, surface);
203
204 window_damage(smoke->window, 0, 0, smoke->width, smoke->height);
205
206 cairo_surface_destroy(surface);
207
208 widget_schedule_redraw(smoke->widget);
209 }
210
211 static void
smoke_motion_handler(struct smoke * smoke,float x,float y)212 smoke_motion_handler(struct smoke *smoke, float x, float y)
213 {
214 int i, i0, i1, j, j0, j1, k, d = 5;
215
216 if (x - d < 1)
217 i0 = 1;
218 else
219 i0 = x - d;
220 if (i0 + 2 * d > smoke->width - 1)
221 i1 = smoke->width - 1;
222 else
223 i1 = i0 + 2 * d;
224
225 if (y - d < 1)
226 j0 = 1;
227 else
228 j0 = y - d;
229 if (j0 + 2 * d > smoke->height - 1)
230 j1 = smoke->height - 1;
231 else
232 j1 = j0 + 2 * d;
233
234 for (i = i0; i < i1; i++)
235 for (j = j0; j < j1; j++) {
236 k = j * smoke->width + i;
237 smoke->b[0].u[k] += 256 - (random() & 512);
238 smoke->b[0].v[k] += 256 - (random() & 512);
239 smoke->b[0].d[k] += 1;
240 }
241 }
242
243 static int
mouse_motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)244 mouse_motion_handler(struct widget *widget, struct input *input,
245 uint32_t time, float x, float y, void *data)
246 {
247 smoke_motion_handler(data, x, y);
248
249 return CURSOR_HAND1;
250 }
251
252 static void
touch_motion_handler(struct widget * widget,struct input * input,uint32_t time,int32_t id,float x,float y,void * data)253 touch_motion_handler(struct widget *widget, struct input *input,
254 uint32_t time, int32_t id, float x, float y, void *data)
255 {
256 smoke_motion_handler(data, x, y);
257 }
258
259 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)260 resize_handler(struct widget *widget,
261 int32_t width, int32_t height, void *data)
262 {
263 struct smoke *smoke = data;
264
265 /* Dont resize me */
266 widget_set_size(smoke->widget, smoke->width, smoke->height);
267 }
268
main(int argc,char * argv[])269 int main(int argc, char *argv[])
270 {
271 struct timespec ts;
272 struct smoke smoke;
273 struct display *d;
274 int size;
275
276 d = display_create(&argc, argv);
277 if (d == NULL) {
278 fprintf(stderr, "failed to create display: %m\n");
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