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