1 /* voronoi.c - Functions for calculating voronoi diagrams,
2 * exclusive of the dialogs
3 *
4 * Copyright (C) 2006 Patrice St-Gelais
5 * patrstg@users.sourceforge.net
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "hf.h"
23 #include "img_process.h"
24 #include "voronoi.h"
25
26 // Variables for list management
27 #ifndef BEGIN_LIST
28 #define BEGIN_LIST -10
29 #endif
30 #ifndef END_LIST
31 #define END_LIST -1
32 #endif
33 #ifndef REALLOC_BLOCK
34 #define REALLOC_BLOCK 10000
35 #endif
36
37 static gint l_begin, l_end, l_allocated_nodes, l_max, l_current, last_node=BEGIN_LIST;
38 static x_y_l * ordered_list=NULL;
39 static gint calls=0, nsteps=0;
40 #define NBSUBINDEX 5000
41 #define FACTORSUBINDEX 5.0
42 static gint *subindex = NULL;
43
voronoi_struct_new(gdouble cell_size,gint distribution_type,gint random_variation,gint hf_use,gboolean gener_noise,gint noise_level,gint crack_width_type,gint min_width,gint max_width,gint edges_level)44 voronoi_struct *voronoi_struct_new (gdouble cell_size, gint distribution_type, gint random_variation, gint hf_use, gboolean gener_noise, gint noise_level, gint crack_width_type, gint min_width, gint max_width, gint edges_level ) {
45 voronoi_struct *vs;
46 vs = (voronoi_struct *) x_calloc(1,sizeof(voronoi_struct), "voronoi_struct");
47 vs->cell_size = MIN(50.0, MAX(0.0, cell_size));
48 if ((distribution_type == UNIFORM) || (distribution_type == CENTERED) || (distribution_type == REGULAR) )
49 vs->distribution_type = distribution_type;
50 else
51 distribution_type = UNIFORM;
52 vs->random_variation = MIN(100,MAX(0,random_variation));
53 if (hf_use==USE_AS_GUIDE)
54 vs->hf_use = hf_use;
55 else
56 vs->hf_use = USE_AS_NOISE;
57 if (gener_noise)
58 vs->gener_noise = TRUE;
59 else
60 vs->gener_noise = FALSE;
61 vs->noise_level = MIN(100,MAX(0,noise_level));
62 if (crack_width_type == FROM_DISTANCE)
63 vs->crack_width_type = crack_width_type;
64 else
65 vs->crack_width_type = FIXED_WIDTH;
66 if (min_width>max_width)
67 min_width = max_width;
68 vs->min_width = MIN(10,MAX(0,min_width));
69 vs->max_width = MIN(10,MAX(0,max_width));
70 vs->edges_level = MIN(100,MAX(0,edges_level));
71 vs->scale = SCALE_1X;
72 vs->noise_opt = subdiv1_opt_new ();
73 return vs;
74 };
75
voronoi_struct_new_with_defaults()76 voronoi_struct *voronoi_struct_new_with_defaults () {
77 return voronoi_struct_new (10.0, UNIFORM, 50, USE_AS_NOISE, TRUE, 50, FIXED_WIDTH, 1, 2, 25);
78 }
79
voronoi_struct_free(voronoi_struct * vs)80 void voronoi_struct_free (voronoi_struct *vs) {
81 if (!vs)
82 return;
83 x_free(vs);
84 }
85
create_site_list(gint max_x,gint max_y,gdouble size,gint distribution,gint random_var,gint * length)86 x_y * create_site_list (gint max_x, gint max_y, gdouble size, gint distribution, gint random_var, gint *length) {
87 // Creates a pixel list in a max_x * max_y grid, randomly distributed
88 // size = average diameter of each cell
89 // Distribution = UNIFORM: uniformly random distribution
90 // Distribution = CENTERED: more pixels in the center
91 x_y *l=NULL;
92 guint i,n, quantumx, quantumy;
93 gint x,y, randomx, randomy;
94 gdouble newsize;
95 // Note: size is assumed to be a % of the dimension
96 if (distribution==REGULAR)
97 n = 2 * (guint) (100.0 / size / 2.0);
98 else
99 n = (guint) (100.0 / size);
100 newsize = 100.0 / (gdouble) n;
101 quantumx = (gint) (((gdouble) max_x) * newsize / 100.0);
102 quantumy = (gint) (((gdouble) max_y) * newsize / 100.0);
103 // For random variation of pixels placement in REGULAR:
104 // Randomx and randomy are upper boundaries for variation, in pixels
105 // 0 % of quantum = 0 % noise
106 randomx = quantumx * 5 * random_var / 100;
107 randomy = quantumy * 5 * random_var / 100;
108 // printf("SIZE: %7.4f; NEWSIZE: %7.4f; NODES: %d; RANDOMX: %d; RANDOMY: %d;\n",size, newsize, n*n, randomx, randomy);
109 l = (x_y *) x_malloc(sizeof(x_y)*n*n, "x_y (l in create_site_list in voronoi.c)");
110
111 for (i=0; i<(n*n); i++) {
112 switch (distribution) {
113 case CENTERED: // 2006-01: quadratic distribution
114 (l+i)->x = (gint) pow((gdouble) ( rand()%((max_x>>1)*(max_x>>1))),0.5);
115 (l+i)->y = (gint) pow((gdouble) ( rand()%((max_y>>1)*(max_y>>1))),0.5);
116 (l+i)->x *= 2*(rand()%2) - 1;
117 (l+i)->y *= 2*(rand()%2) - 1;
118 (l+i)->x = ((l+i)->x+max_x)%max_x;
119 (l+i)->y = ((l+i)->y+max_x)%max_x;
120 break;
121 case UNIFORM:
122 (l+i)->x = rand()%max_x;
123 (l+i)->y = rand()%max_y;
124 break;
125 case REGULAR: // Kind of hexagonal
126 x = i%n;
127 y = i/n;
128 if (!(y%2)) {// Translate half the size
129 (l+i)->x = WRAP(x * quantumx + (quantumx>>1), max_x);
130 // printf("Translate %d\n",x);
131 }
132 else
133 (l+i)->x = x * quantumx;
134 (l+i)->y = y * quantumy;
135 // Random factor
136 if (randomx)
137 (l+i)->x = WRAP((l+i)->x+rand()%randomx, max_x);
138 if (randomy)
139 (l+i)->y = WRAP((l+i)->y+rand()%randomy, max_y);
140 // printf("X,Y: %d,%d; x,y: %d,%d\n",x,y,(l+i)->x,(l+i)->y);
141 }
142 }
143
144 (*length) = n*n;
145 return l;
146 }
147
voronoi_adjust_edges(hf_type * in,hf_type * out,hf_type * dist,gint edges_level,gint max_x,gint max_y)148 void voronoi_adjust_edges (hf_type *in, hf_type *out, hf_type *dist, gint edges_level, gint max_x, gint max_y) {
149 // We lift the edges of the cells by adding the distance from
150 // the cell centers to the master grid
151 gint i;
152 gdouble value, ratio;
153 ratio = ((gdouble) edges_level) / 100.0;
154 // printf("VORONOI_ADJUST_EDGES\n");
155 for (i=0; i<(max_x*max_y); i++) {
156 if (!*(in+i)) {
157 *(out+i) = 0;
158 continue;
159 }
160 value = ratio * (gdouble) *(dist+i);
161 *(out+i) = *(in+i) + (hf_type) value;
162 }
163 }
164
voronoi_dist_clamp(hf_type * hf_buf,gint max_x,gint max_y,hf_type newmin,hf_type newmax,gdouble * dbuf)165 void voronoi_dist_clamp (hf_type *hf_buf, gint max_x, gint max_y, hf_type newmin, hf_type newmax, gdouble *dbuf) {
166 // From hf_calc::double_clamp
167 // We raise the input to its square
168 gdouble ratio, min, max=0.0, newmind, sum=0.0;
169 static gdouble maxd = (gdouble) MAX_HF_VALUE;
170 gint i;
171 newmind = (gdouble) newmin;
172 min = maxd;
173 for (i=0; i<max_x*max_y; i++) {
174 sum+=*(dbuf+i);
175 if (*(dbuf+i)<min)
176 min = *(dbuf+i);
177 if (*(dbuf+i)>max)
178 max = *(dbuf+i);
179 }
180 // printf("MIN: %f; MAX: %f; AVRG: %f\n",min,max, sum / (gdouble) (max_x*max_y));
181 min *= min;
182 max *= max;
183 ratio = ((gdouble) ABS(newmax-newmin)) / (max - min);
184 // printf("MIN: %f; MAX: %f; RATIO: %f\n",min,max,ratio);
185 for (i=0; i< max_x*max_y; i++) {
186 *(hf_buf+i) = (hf_type) ( (ratio * ( (*(dbuf+i) * *(dbuf+i)) - min) + newmind));
187 }
188 }
189
190
reset_list(gint length)191 void reset_list (gint length) {
192 if (ordered_list)
193 x_free(ordered_list);
194 ordered_list = (x_y_l*) x_calloc(sizeof(x_y_l), length, "x_y_l (ordered_list in reset_list in voronoi.c)");
195 l_allocated_nodes = length;
196 last_node = BEGIN_LIST;
197 calls = 0;
198
199 if (subindex)
200 x_free(subindex);
201
202 subindex = (gint *) x_calloc(NBSUBINDEX,sizeof(gint), "gint (subindex in reset_list)");
203
204 // printf("Allocating %d nodes\n",length);
205 }
206
add_node(gint x,gint y,gdouble weigth)207 void add_node (gint x, gint y, gdouble weigth) {
208 // We insert the node before the first location
209 // whose weigth > weigth to add
210 gint index;
211 calls++;
212
213 if (last_node==BEGIN_LIST)
214 last_node = l_current;
215
216 index = *(subindex + MIN(NBSUBINDEX-1,(gint) (weigth*FACTORSUBINDEX)));
217
218 if (!index)
219 index = *(subindex + MIN(NBSUBINDEX-1, 10 * (gint) (weigth*FACTORSUBINDEX/10.0)));
220 if (!index)
221 index = last_node;
222
223 // Index (last found target) is kept between each call
224 // Generally the node to find is close to the last we've found
225 // If weigth of index is > weigth to look for, we search backward
226 // If weigth of index is <, we search forward
227 if (weigth > (ordered_list+index)->weigth) { // Search forward
228 while ( ((ordered_list+index)->next != END_LIST) && (weigth > (ordered_list+index)->weigth ) ) {
229 index = (ordered_list+index)->next;
230 nsteps++;
231
232 }
233 }
234 else { // Search backward
235 while ( ((ordered_list+index)->prec != BEGIN_LIST) && (weigth < (ordered_list+index)->weigth ) ) {
236 index = (ordered_list+index)->prec;
237 nsteps++;
238 }
239 if (index != BEGIN_LIST) {
240 if ((ordered_list+index)->next != END_LIST)
241 index = (ordered_list+index)->next;
242 }
243 else
244 index = l_begin;
245 }
246 l_max = l_max + 1;
247 if (l_allocated_nodes<=l_max) {
248 l_allocated_nodes += REALLOC_BLOCK;
249 ordered_list = (x_y_l*) x_realloc(ordered_list, l_allocated_nodes*sizeof(x_y_l), "x_y_l (ordered_list - realloc)");
250 }
251 (ordered_list+l_max-1)->x = x;
252 (ordered_list+l_max-1)->y = y;
253 (ordered_list+l_max-1)->weigth = weigth;
254 // index == -1?? (END_LIST)
255 if ( ((ordered_list+index)->next==END_LIST) && ((ordered_list+index)->weigth < weigth) ) {
256 // Add a node at the end
257 // Index points to the last node
258 // printf("Insert %d at the end - index: %d\n",l_max-1, index);
259 (ordered_list+l_max-1)->prec = index;
260 (ordered_list+l_max-1)->next = END_LIST;
261 (ordered_list+index)->next = l_max-1;
262 l_end = l_max;
263 }
264 else {
265 // Insert the node before the current one
266 // printf("Insert %d before %d\n",l_max-1,index);
267 (ordered_list+l_max-1)->prec = (ordered_list+index)->prec;
268 (ordered_list+l_max-1)->next = index;
269 if ((ordered_list+index)->prec != BEGIN_LIST)
270 (ordered_list+(ordered_list+index)->prec)->next = l_max-1;
271 (ordered_list+index)->prec = l_max-1;
272 }
273 // if (index==l_begin)
274 // printf("******************* Insert %d before l_begin (%d)\n",l_max-1,l_begin);
275 last_node = index;
276 *(subindex+MIN(NBSUBINDEX-1,(gint) (weigth*FACTORSUBINDEX))) = index;
277 if (!*(subindex+MIN(NBSUBINDEX-1, 10 * (gint) (weigth*FACTORSUBINDEX/10.0))))
278 *(subindex + MIN(NBSUBINDEX-1, 10 * (gint) (weigth*FACTORSUBINDEX/10.0))) = index;
279 }
280
delete_node(gint index)281 void delete_node (gint index) {
282 // Index is in base 0
283 // The nodes are not physically destroyed
284 // We only change the pointers to "shortcut" the current node
285
286 // Change the next pointer in the preceding node
287 if (index != l_begin) {
288 if (index == l_end) {
289 (ordered_list+(ordered_list+index)->prec)->next = END_LIST;
290 l_end = (ordered_list+index)->prec;
291 }
292 else {
293 (ordered_list+(ordered_list+index)->prec)->next = (ordered_list+index)->next;
294 }
295 }
296
297 // Change the preceding pointer in the next node
298 if (index != l_end) {
299 if (index == l_begin) {
300 (ordered_list+(ordered_list+index)->next)->prec = BEGIN_LIST;
301 l_begin = (ordered_list+index)->next;
302 }
303 else {
304 (ordered_list+(ordered_list+index)->next)->prec = (ordered_list+index)->prec;
305 }
306 }
307 }
308
expand_seeds(gdouble * dist,gint * sites,gint * nodes,hf_type * noise_in,hf_type * guide_in,gint max_x,gint max_y,gint length,gdouble nlevel)309 void expand_seeds (gdouble *dist, gint *sites, gint *nodes, hf_type *noise_in, hf_type *guide_in, gint max_x, gint max_y, gint length, gdouble nlevel) {
310
311 hf_type site, *guide=NULL, *noise=NULL;
312 gint n, x, y, xx, yy, index, shift;
313 gdouble dx, dy, wx, wy, weigth, new_dist, base_noise, nnoise;
314 x_y_l pixel;
315
316 // printf ("LENGTH: %d; NLEVEL: %7.3f\n",length, nlevel);
317 // 2. Expand the seeds up to common borders with areas defined by neighbour seeds
318 // We loop through the list until all the current pixels
319 // are seen, and there are no new neighbour pixels to add
320 // This step outputs 1 main structure:
321 // dist = distances matrix - the maxima (crests) define the borders
322
323 if (noise_in) {
324 noise = (hf_type *) x_malloc(sizeof(hf_type)*max_x*max_y, "hf_type (noise_in in expand_seeds in voronoi.c)");
325 memcpy(noise, noise_in, sizeof(hf_type)*max_x*max_y);
326 }
327 if (guide_in) {
328 guide = (hf_type *) x_malloc(sizeof(hf_type)*max_x*max_y, "hf_type (guide in expand_seeds in voronoi.c)");
329 memcpy(guide, guide_in, sizeof(hf_type)*max_x*max_y);
330 }
331
332 // l_* variables are global, static
333
334 // l_max is always the next node to initialize (we're in base 0!)
335 l_max = length;
336
337 l_begin = 0;
338 l_end = l_max;
339 l_current = 0;
340
341 // printf("Begin: %d; End: %d; Current: %d; Max: %d\n", l_begin, l_end, l_current, l_max);
342 // l_current is always the address of the node with the lowest weight (dist+noise)
343
344 while (l_current!=END_LIST) {
345
346 memcpy(&pixel,ordered_list + l_current,sizeof(x_y_l));
347 index = (WRAP2(pixel.y,max_y)*max_x) + WRAP2(pixel.x,max_x);
348
349 // The current site is always the one with the lowest distance
350 // Pixels visited as neighbours (frontier) have negative site number
351 // We remove the processed site from the frontier (not anymore negative)
352
353 *(sites+index) = ABS(*(sites+index));
354
355 // Process each neighbour (crux shape)
356 // (x-1, y), (x, y-1), (x, y+1), (x+1, y)
357 // We add the neighbours to the heap
358 // For each neighbour, check if it's in a region
359 // Cases:
360 // (1) It's not in any region (site # == 0)
361 // ==> add it to the frontier
362 // (2) It's in a region with the same #, but negative
363 // ==> it's already listed as a neighbour, don't add it
364 // (3) It's in the same region, positive number
365 // ==> already processed, don't add it
366 // (4) It's in another region, positive number
367 // If the weigth is higher, add it to the frontier
368
369 weigth = pixel.weigth;
370 (ordered_list+l_current)->weigth = 0.0;
371
372 shift = ABS(pixel.y)%2; // We shift indexes for odd rows (even rows have been shifted right half a pixel)
373
374 for (n=1; n<7; n++) {
375 switch (n) {
376 //
377 // 5 | 6
378 // 4 | 0 | 1
379 // 3 | 2
380 //
381
382 case 1:
383 xx = pixel.x + 1;
384 yy = pixel.y;
385 dx = 1.0;
386 dy = 0.0;
387 break;
388 case 2:
389 xx = pixel.x + shift;
390 yy = pixel.y + 1;
391 dx = 0.5;
392 dy = 1.0;
393 break;
394 case 3:
395 xx = pixel.x - 1 + shift;
396 yy = pixel.y + 1;
397 dx = -0.5;
398 dy = 1.0;
399 break;
400 case 4:
401 xx = pixel.x - 1;
402 yy = pixel.y;
403 dx = -1.0;
404 dy = 0.0;
405 break;
406 case 5:
407 xx = pixel.x - 1 + shift;
408 yy = pixel.y - 1;
409 dx = -0.5;
410 dy = -1.0;
411 break;
412 case 6:
413 xx = pixel.x + shift;
414 yy = pixel.y - 1;
415 dx = 0.5;
416 dy = -1.0;
417 }
418 x = WRAP2(xx,max_x);
419 y = WRAP2(yy,max_y);
420 // Test if pixel not assigned or assigned to a different site
421 if (ABS(*(sites + x + y*max_x))==*(sites+index))
422 continue;
423 wx = pixel.wx + dx;
424 wy = pixel.wy + dy;
425 if (*(sites + x + y*max_x) != *(sites+index)) {
426 site = ABS(*(sites+index))-1; // "site" is a base-0 index, while *sites contains site IDs (base 1)
427 base_noise = 0.0;
428 if (noise)
429 base_noise = *(noise+x+y*max_x) ;
430 if (guide)
431 base_noise += 5.0 * *(guide+x+y*max_x);
432 nnoise = nlevel * base_noise / 100.0;
433
434 new_dist = sqrt (wx*wx + wy*wy);
435
436 if (!*(sites + x + y*max_x)) { // New assignment
437 // Assign site, evaluate distance, add to frontier
438 // Site number is negative for pixels which
439 // are part of a frontier
440 *(sites + x + y*max_x) = -*(sites+index);
441 *(dist + x + y*max_x) = DIST2(xx,yy,(ordered_list+site)->x, (ordered_list+site)->y);
442 if (guide && *(guide + x + y*max_x))
443 *(dist + x + y*max_x) += 5.0;
444
445 // Add to frontier
446 add_node ( xx, yy, new_dist + nnoise );
447
448 (ordered_list+l_max-1)->wx = wx + dx;
449 (ordered_list+l_max-1)->wy = wy + dy;
450 *(nodes + x + y*max_x) = l_max-1;
451 }
452 else { // Already assigned
453 // 2 cases:
454 // 1. It's a negative site (belongs to a frontier)
455 // ==> assign to the current frontier
456 // 2. It's a positive site
457 // ==> do nothing, it's "frozen"
458
459 if ((*(sites + x + y*max_x) < 0) && ((new_dist+nnoise) < weigth)) {
460 *(sites + x + y*max_x) = -*(sites+index);
461 *(dist + x + y*max_x) = DIST2(xx,yy,(ordered_list+site)->x, (ordered_list+site)->y);
462 if (guide && *(guide + x + y*max_x))
463 *(dist + x + y*max_x) += 5.0;
464 // It must be "unwrapped"
465 (ordered_list + *(nodes + x + y*max_x))->x = xx;
466 (ordered_list + *(nodes + x + y*max_x))->y = yy;
467
468 (ordered_list+*(nodes + x + y*max_x))->wx = wx + dx;
469 (ordered_list+*(nodes + x + y*max_x))->wy = wy + dy;
470 (ordered_list+*(nodes + x + y*max_x))->weigth = new_dist + nnoise;
471 }
472
473 }
474 }
475 }
476 l_current = (ordered_list+l_current)->next;
477 if (l_current != END_LIST)
478 delete_node((ordered_list+l_current)->prec);
479
480 } // End while
481
482 if (noise)
483 x_free(noise);
484 if (guide)
485 x_free(guide);
486
487 }
488
assign_seeds(gint * nodes,gint * sites,gint max_x,gint max_y,x_y * seed_list,gint length)489 void assign_seeds (gint *nodes, gint *sites, gint max_x, gint max_y, x_y *seed_list, gint length) {
490
491 gint n;
492
493 // We manage our own ordered list
494 // (using GSList makes no sense from a performance standpoint!)
495
496 // Each pixel is visited at least once, so we allocate
497 // a full max_x*max_y ordered list struct.
498 // Then we reallocate by blocks of 10000 pixels when needed
499
500 reset_list(max_x*max_y);
501
502 // 1. Assign the sites seed
503
504 for (n=0; n<length; n++) {
505 // We start with the list of seeds
506 (ordered_list+n)->prec = n-1;
507 (ordered_list+n)->next = n+1;
508 (ordered_list+n)->x = (seed_list+n)->x;
509 (ordered_list+n)->y = (seed_list+n)->y;
510 (ordered_list+n)->weigth = 0.0;
511 // Base 1 (no 0 allowed as site number - 0 means not assigned)
512 *(sites + (seed_list+n)->y*max_x + (seed_list+n)->x) = n+1;
513 // We don't assign distance, since it's already 0
514 // printf("Site #%d: (%d, %d)\n",n+1, (seed_list+n)->x, (seed_list+n)->y);
515 *(nodes + (seed_list+n)->y*max_x + (seed_list+n)->x) = n;
516 }
517
518 (ordered_list+n-1)->next = END_LIST; // End of the list
519 ordered_list->prec = BEGIN_LIST; // First node
520 }
521
draw_cracks(hf_type * output,gdouble * dist,gint * sites,gint max_x,gint max_y,gint max_width,gint crack_width_type)522 void draw_cracks (hf_type *output, gdouble *dist, gint *sites, gint max_x, gint max_y, gint max_width, gint crack_width_type) {
523
524 gint x;
525 gdouble *v;
526 // Draw cracks as 1-pixel width lines
527 // Cracks are crests (distance maxima separating areas - areas borders)
528 if (max_width>0) {
529 v = (gdouble *) x_malloc(sizeof(gdouble)*max_x*max_y, "gdouble (v in draw_cracks in voronoi.c)");
530 // We add an epsilon to force inequality at sites border
531 for (x=0; x<(max_x*max_y); x++)
532 *(v+x) = *(dist+x) + ((gdouble) *(sites+x)) / 5.0 ;
533
534 if (crack_width_type==FIXED_WIDTH) {
535 // In the output: lines (edge values) are initialized at 0, the balance at VORONOI_HEIGHT
536 dfind_maximum (v, output, max_x, max_y, 0, VORONOI_HEIGHT);
537 }
538 else { // FROM_DISTANCE: edges values == distance, balance = 0
539 dfind_maximum (v, output, max_x, max_y, 0, 0);
540 }
541 x_free(v);
542 }
543 else // No width - initialize *output to VORONOI_HEIGHT
544 for (x=0; x<(max_x*max_y); x++)
545 *(output+x) = VORONOI_HEIGHT;
546 }
547
widen_cracks(hf_type * output,gint max_x,gint max_y,gint min_width,gint max_width,gint crack_width_type)548 void widen_cracks (hf_type *output, gint max_x, gint max_y, gint min_width, gint max_width, gint crack_width_type) {
549
550 hf_type *tmp;
551 gint x;
552
553 // Widen the cracks
554 if (max_width>1) {
555 tmp = (hf_type *) x_malloc(sizeof(hf_type)*max_x*max_y, "hf_type (tmp in widen_cracks in voronoi.c)");
556 memcpy(tmp,output,sizeof(hf_type)*max_x*max_y);
557 if (crack_width_type==FIXED_WIDTH) {
558 min_max_spread (tmp, output, max_x, max_y, max_width, TRUE);
559 }
560 else { // FROM_DISTANCE: we "spread" the maxima
561 spread_over_max (tmp, output, max_x, max_y, TRUE, min_width, max_width, 0, 0, TENT_FUNC);
562 for (x=0; x<(max_x*max_y); x++)
563 if (*(output+x)>0)
564 *(output+x) = 0;
565 else
566 *(output+x) = VORONOI_HEIGHT;
567 }
568 x_free(tmp);
569 }
570 }
571
voronoi(hf_type * output,hf_type * output_dist,hf_type * noise_in,hf_type * guide_in,gint max_x,gint max_y,gdouble size,gint distribution,gint random_var,gint crack_width_type,gint min_width,gint max_width,gboolean gener_noise,gint noise_level,gboolean dist_required)572 void voronoi (hf_type *output, hf_type *output_dist, hf_type *noise_in, hf_type *guide_in, gint max_x, gint max_y, gdouble size, gint distribution, gint random_var, gint crack_width_type, gint min_width, gint max_width, gboolean gener_noise, gint noise_level, gboolean dist_required) {
573
574 gint *sites=NULL, *nodes=NULL, length;
575 gdouble *dist=NULL;
576 x_y *seed_list=NULL;
577
578 // Distances matrix - the maxima define the areas borders
579 dist = (gdouble *) x_calloc(sizeof(gdouble),max_x*max_y, "gdouble (dist in voronoi)");
580 // Sites matrix - each site has a different number, from 1 to "length"
581 // Used to test if a pixel is already assigned to a site
582 sites = (gint *) x_calloc(sizeof(gint),max_x*max_y, "gint (sites in voronoi)");
583 // Nodes matrix - index of each pixel in the ordered list
584 nodes = (gint *) x_calloc(sizeof(gint),max_x*max_y, "gint (nodes in voronoi)");
585
586 seed_list = create_site_list (max_x, max_y, size, distribution, random_var, &length);
587
588 // printf("LENGTH: %d; SIZE: %7.3f\n",length, size);
589
590 // << assign_seeds>
591 // Output:
592 // ordered_list (static, global, preallocated),
593 // nodes (preallocated)
594 // sites (preallocated)
595 assign_seeds (nodes, sites, max_x, max_y, seed_list, length);
596
597 // << expand_seeds >>
598 // Input:
599 // Output: ordered_list, dist, sites
600
601 // 2006-11-14: The noise level is scaled relatively to the cell size
602 // Reference: cell size of 10% is 1.
603 expand_seeds (dist, sites, nodes, noise_in, guide_in, max_x, max_y, length, ((gdouble) noise_level) / 100.0);
604
605 draw_cracks (output, dist, sites, max_x, max_y, max_width, crack_width_type);
606
607 widen_cracks (output, max_x, max_y, min_width, max_width, crack_width_type);
608
609 // Adjust distance, so that it can be added with the crack array for
610 // simulating the edges lifting of each area
611 if (dist_required)
612 voronoi_dist_clamp (output_dist, max_x, max_y, 0, MAX_HF_VALUE-VORONOI_HEIGHT, dist);
613
614 if (seed_list)
615 x_free(seed_list);
616 if (sites)
617 x_free(sites);
618 if (dist)
619 x_free(dist);
620 if (nodes)
621 x_free(nodes);
622
623 }
624
voronoi_revert(hf_type * output,hf_type * input,gint length)625 void voronoi_revert(hf_type *output, hf_type *input, gint length) {
626 // Special revert function
627 // Black -> white
628 // Others ==
629 gint i;
630 for (i=0; i<length; i++) {
631 if (!*(input+i))
632 *(output+i) = MAX_HF_VALUE;
633 else
634 *(output+i) = *(input+i);
635 }
636 }
voronoi_undo_revert(hf_type * output,hf_type * input,gint length)637 void voronoi_undo_revert(hf_type *output, hf_type *input, gint length) {
638 // See the preceding function
639 gint i;
640 for (i=0; i<length; i++) {
641 if (*(input+i)==MAX_HF_VALUE)
642 *(output+i) = 0;
643 else
644 *(output+i) = *(input+i);
645 }
646 }
647
hf_voronoi(hf_struct_type * hf,voronoi_struct * vs)648 void hf_voronoi (hf_struct_type *hf, voronoi_struct *vs) {
649
650 gdouble scaled_cell_size;
651 gint scaled_min_width, scaled_max_width;
652 hf_type *tmp=NULL, *tmp_cracks=NULL, *guide, *noise;
653
654 // Convention:
655 // 1. tmp_buf: original HF
656 // 2. tmp2_buf: distance HF
657 // 3. result_buf: crackled HF
658 // 4. hf_buf: output = crackled HF + distance HF (for lifting edges)
659 // hf_buf is computed outside this function
660
661 if (!hf->tmp_buf)
662 hf_backup(hf);
663 if (!hf->result_buf) {
664 hf->result_buf = xalloc_hf_type(hf->max_x, "hf_type (result_buf in hf_voronoi)");
665 }
666 if (!hf->tmp2_buf) {
667 hf->tmp2_buf = xalloc_hf_type(hf->max_x, "hf_type (tmp2_buf in hf_voronoi)");
668 }
669
670 // scaled_size is required for managing the multiple scale option
671 // The biggest scale (4x or 2x) should be < 50 %
672 // So, we scale down the original cell size to get it rigth, when required
673
674 switch (vs->scale) {
675 case SCALE_1X:
676 scaled_cell_size = vs->cell_size;
677 scaled_min_width = vs->min_width;
678 scaled_max_width = vs->max_width;
679 break;
680 case SCALE_1X_2X:
681 scaled_cell_size = MIN(vs->cell_size*2.0,49.0);
682 scaled_min_width = MIN(vs->min_width*2,10);
683 scaled_max_width = MIN(vs->max_width*2,10);
684 break;
685 case SCALE_1X_2X_4X:
686 scaled_cell_size = MIN(vs->cell_size*4.0,49.0);
687 scaled_min_width = MIN(vs->min_width*4,10);
688 scaled_max_width = MIN(vs->max_width*4,10);
689 break;
690 default:
691 scaled_cell_size = vs->cell_size;
692 }
693
694 srand(vs->seed);
695
696 if (vs->hf_use==USE_AS_NOISE) {
697 noise = hf->tmp_buf;
698 guide = NULL;
699 }
700 else { // USE_AS_GUIDE
701 guide = hf->tmp_buf;
702 if (vs->gener_noise) {
703 tmp = xalloc_hf_type(hf->max_x, "hf_type (tmp in hf_voronoi)");
704 calc_subdiv1 (tmp, hf->max_x, hf->max_y, vs->noise_opt);
705 noise = tmp;
706 }
707 else {
708 noise = NULL;
709 }
710 }
711
712 voronoi (hf->result_buf, hf->tmp2_buf, noise, guide, hf->max_x, hf->max_y, scaled_cell_size, vs->distribution_type, vs->random_variation, vs->crack_width_type, scaled_min_width, scaled_max_width, vs->gener_noise, vs->noise_level, (vs->scale==SCALE_1X));
713
714 if (vs->distribution_type==REGULAR) {
715 // Improve edges (remove some line artifacts)
716 if (scaled_max_width>0) {
717 if (tmp) x_free(tmp);
718 tmp = xalloc_hf_type(hf->max_x, "hf_type (tmp in hf_voronoi)");
719 memcpy(tmp,hf->result_buf,sizeof(hf_type)*hf->max_x*hf->max_y);
720 improve_edges(tmp, hf->result_buf, hf->max_x, hf->max_y, TRUE, VORONOI_HEIGHT-1);
721 }
722 if (tmp)
723 x_free(tmp);
724 return;
725 }
726
727 // 2x scale
728 if (vs->scale!=SCALE_1X) {
729 tmp_cracks = xalloc_hf_type(hf->max_x, "hf_type (tmp_cracks in hf_voronoi)");
730 voronoi_revert(tmp_cracks,hf->result_buf,hf->max_x*hf->max_y);
731 voronoi (hf->result_buf, hf->tmp2_buf, noise, tmp_cracks, hf->max_x, hf->max_y, scaled_cell_size / 2.0, vs->distribution_type, vs->random_variation, vs->crack_width_type, scaled_min_width / 2, scaled_max_width / 2, vs->gener_noise, vs->noise_level, vs->scale==SCALE_1X_2X);
732 // Merge hf->result_buf and tmp_cracks in hf->result_buf
733 voronoi_undo_revert(tmp_cracks,tmp_cracks,hf->max_x*hf->max_y);
734 hf_min_merge(hf->result_buf, hf->result_buf, tmp_cracks, hf->max_x*hf->max_x);
735 }
736
737 // 4x scale
738 if (vs->scale==SCALE_1X_2X_4X) {
739 tmp_cracks = xalloc_hf_type(hf->max_x, "hf_type (tmp_cracks in hf_voronoi)");
740 voronoi_revert(tmp_cracks,hf->result_buf,hf->max_x*hf->max_y);
741 voronoi (hf->result_buf, hf->tmp2_buf, noise, tmp_cracks, hf->max_x, hf->max_y, scaled_cell_size / 4.0, vs->distribution_type, vs->random_variation, vs->crack_width_type, scaled_min_width / 4, scaled_max_width / 4, vs->gener_noise, vs->noise_level,vs->scale==SCALE_1X_2X_4X);
742 // Merge hf->result_buf and tmp_cracks in hf->result_buf
743 voronoi_undo_revert(tmp_cracks,tmp_cracks,hf->max_x*hf->max_y);
744 hf_min_merge(hf->result_buf, hf->result_buf, tmp_cracks, hf->max_x*hf->max_x);
745 }
746 // Improve edges (remove some line artifacts)
747 if (scaled_max_width>0) {
748 if (tmp) x_free(tmp);
749 tmp = xalloc_hf_type(hf->max_x, "hf_type (tmp in hf_voronoi)");
750 memcpy(tmp,hf->result_buf,sizeof(hf_type)*hf->max_x*hf->max_y);
751 improve_edges(tmp, hf->result_buf, hf->max_x, hf->max_y, TRUE, VORONOI_HEIGHT-1);
752 }
753 if (tmp)
754 x_free(tmp);
755 if (tmp_cracks)
756 x_free(tmp_cracks);
757 };
758