Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
make_map.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#define STANDALONE
12#include <assert.h>
13#include <sparse/SparseMatrix.h>
14#include <sparse/general.h>
15#include <math.h>
16#include <sparse/QuadTree.h>
17#include <stdbool.h>
18#include <stddef.h>
19#include <string.h>
20#include <cgraph/list.h>
21#include <cgraph/cgraph.h>
22#include "make_map.h"
25#include <sparse/colorutil.h>
26#include <neatogen/delaunay.h>
27#include <util/agxbuf.h>
28#include <util/alloc.h>
29#include <util/prisize_t.h>
30
31#include <edgepaint/lab.h>
33
34void map_palette_optimal_coloring(char *color_scheme, SparseMatrix A0,
35 float **rgb_r, float **rgb_g, float **rgb_b){
36 /*
37 for a graph A, get a distinctive color of its nodes so that the color distanmce among all nodes are maximized. Here
38 color distance on a node is defined as the minimum of color differences between a node and its neighbors.
39 color_scheme: rgb, gray, lab, or one of the color palettes in color_palettes.h, or a list of hex rgb colors separaterd by comma like "#ff0000,#00ff00"
40 A: the graph of n nodes
41 cdim: dimension of the color space
42 rgb_r, rgb_g, rgb_b: float array of length A->m + 1, which contains color for each country. 1-based
43 */
44
45 /*color: On input an array of size n*cdim, if NULL, will be allocated. On exit the final color assignment for node i is [cdim*i,cdim*(i+1)), in RGB (between 0 to 1)
46 */
47 double *colors = NULL;
48 int n = A0->m, i, cdim;
49
51 bool weightedQ = true;
52
53 {double *dist = NULL;
54 A = SparseMatrix_symmetrize(A0, false);
58 free(dist);
60 SparseMatrix_export(stdout, A);
61 }
62
63 // lightness: of the form 0,70, specifying the range of lightness of LAB
64 // color. Ignored if scheme is not COLOR_LAB.
65 int lightness[] = {0, 100};
66
67 // accuracy is the threshold given so that when finding the coloring for each
68 // node, the optimal is with in "accuracy" of the true global optimal.
69 const double accuracy = 0.01;
70
71 // seed: random_seed. If negative, consider -seed as the number of random
72 // start iterations
73 const int seed = -10;
74
75 node_distinct_coloring(color_scheme, lightness, weightedQ, A, accuracy, seed,
76 &cdim, &colors);
77
78 if (A != A0){
80 }
81 *rgb_r = gv_calloc(n + 1, sizeof(float));
82 *rgb_g = gv_calloc(n + 1, sizeof(float));
83 *rgb_b = gv_calloc(n + 1, sizeof(float));
84
85 for (i = 0; i < n; i++){
86 (*rgb_r)[i+1] = (float) colors[cdim*i];
87 (*rgb_g)[i+1] = (float) colors[cdim*i + 1];
88 (*rgb_b)[i+1] = (float) colors[cdim*i + 2];
89 }
90 free(colors);
91}
92
93void map_optimal_coloring(int seed, SparseMatrix A, float *rgb_r, float *rgb_g, float *rgb_b){
94 int *p = NULL;
95 float *u = NULL;
96 int n = A->m;
97 int i;
98
100
101 rgb_r++; rgb_b++; rgb_g++;/* seems necessary, but need to better think about cases when clusters are not contiguous */
102 vector_float_take(n, rgb_r, n, p, &u);
103 for (i = 0; i < n; i++) rgb_r[i] = u[i];
104 vector_float_take(n, rgb_g, n, p, &u);
105 for (i = 0; i < n; i++) rgb_g[i] = u[i];
106 vector_float_take(n, rgb_b, n, p, &u);
107 for (i = 0; i < n; i++) rgb_b[i] = u[i];
108 free(u);
109}
110
111static int get_poly_id(int ip, SparseMatrix point_poly_map){
112 return point_poly_map->ja[point_poly_map->ia[ip]];
113}
114
115void improve_contiguity(int n, int dim, int *grouping, SparseMatrix poly_point_map, double *x, SparseMatrix graph){
116 /*
117 grouping: which group each of the vertex belongs to
118 poly_point_map: a matrix of dimension npolys x (n + nrandom), poly_point_map[i,j] != 0 if polygon i contains the point j.
119 . If j < n, it is the original point, otherwise it is artificial point (forming the rectangle around a label) or random points.
120 */
121 int i, j, *ia, *ja, u, v;
122 SparseMatrix point_poly_map, D;
123 double dist;
124 int nbad = 0, flag;
125 int maxit = 10;
126
128
129 assert(graph->m == n);
130 ia = D->ia; ja = D->ja;
131 double *a = D->a;
132
133 /* point_poly_map: each row i has only 1 entry at column j, which says that point i is in polygon j */
134 point_poly_map = SparseMatrix_transpose(poly_point_map);
135
136 for (i = 0; i < n; i++){
137 u = i;
138 for (j = ia[i]; j < ia[i+1]; j++){
139 v = ja[j];
140 dist = distance_cropped(x, dim, u, v);
141 if (grouping[u] != grouping[v]){
142 a[j] = 1.1*dist;
143 } else if (get_poly_id(u, point_poly_map) == get_poly_id(v, point_poly_map)){
144 a[j] = dist;
145 } else {
146 nbad++;
147 a[j] = 0.9*dist;
148 }
149
150 }
151 }
152
153 if (Verbose) fprintf(stderr,"ratio (edges among discontiguous regions vs total edges)=%f\n",((double) nbad)/ia[n]);
154 stress_model(dim, D, &x, maxit, &flag);
155
156 assert(!flag);
157
159 SparseMatrix_delete(point_poly_map);
160}
161
162struct Triangle {
163 int vertices[3];/* 3 points */
164 double center[2]; /* center of the triangle */
165};
166
167static void normal(double v[], double normal[]){
168 if (v[0] == 0){
169 normal[0] = 1; normal[1] = 0;
170 } else {
171 normal[0] = -v[1];
172 normal[1] = v[0];
173 }
174}
175
176static void triangle_center(double x[], double y[], double z[], double c[]){
177 /* find the "center" c, which is the intersection of the 3 vectors that are normal to each
178 of the edges respectively, and which passes through the center of the edges respectively
179 center[{x_, y_, z_}] := Module[
180 {xy = 0.5*(x + y), yz = 0.5*(y + z), zx = 0.5*(z + x), nxy, nyz,
181 beta, cen},
182 nxy = normal[y - x];
183 nyz = normal[y - z];
184 beta = (y-x).(xy - yz)/(nyz.(y-x));
185 cen = yz + beta*nyz;
186 Graphics[{Line[{x, y, z, x}], Red, Point[cen], Line[{cen, xy}],
187 Line[{cen, yz}], Green, Line[{cen, zx}]}]
188
189 ]
190 */
191 double xy[2], yz[2], nxy[2], nyz[2], ymx[2], ymz[2], beta, bot;
192 int i;
193
194 for (i = 0; i < 2; i++) ymx[i] = y[i] - x[i];
195 for (i = 0; i < 2; i++) ymz[i] = y[i] - z[i];
196 for (i = 0; i < 2; i++) xy[i] = 0.5*(x[i] + y[i]);
197 for (i = 0; i < 2; i++) yz[i] = 0.5*(y[i] + z[i]);
198
199
200 normal(ymx, nxy);
201 normal(ymz, nyz);
202 bot = nyz[0]*(x[0]-y[0])+nyz[1]*(x[1]-y[1]);
203 if (bot == 0){/* xy and yz are parallel */
204 c[0] = xy[0]; c[1] = xy[1];
205 return;
206 }
207 beta = ((x[0] - y[0])*(xy[0] - yz[0])+(x[1] - y[1])*(xy[1] - yz[1]))/bot;
208 c[0] = yz[0] + beta*nyz[0];
209 c[1] = yz[1] + beta*nyz[1];
210}
211
212static SparseMatrix matrix_add_entry(SparseMatrix A, int i, int j, int val){
213 int i1 = i, j1 = j;
214 if (i < j) {
215 i1 = j; j1 = i;
216 }
218 return SparseMatrix_coordinate_form_add_entry(A, i1, j1, &val);
219}
220
221static void plot_dot_edges(FILE *f, SparseMatrix A){
222 int i, *ia, *ja, j;
223
224
225 int n = A->m;
226 ia = A->ia;
227 ja = A->ja;
228 for (i = 0; i < n; i++){
229 for (j = ia[i]; j < ia[i+1]; j++){
230 if (ja[j] == i) continue;
231 fprintf(f,"%d -- %d;\n",i,ja[j]);
232 }
233 }
234}
235
236static void plot_dot_labels(FILE *f, int n, int dim, double *x, char **labels, float *fsz){
237 int i;
238
239 for (i = 0; i < n; i++){
240 if (fsz){
241 fprintf(f, "%d [label=\"%s\", pos=\"%lf,%lf\", fontsize=%f];\n",i, labels[i], x[i*dim], x[i*dim+1], fsz[i]);
242 } else {
243 fprintf(f, "%d [label=\"%s\", pos=\"%lf,%lf\"];\n",i, labels[i], x[i*dim], x[i*dim+1]);
244 }
245 }
246
247}
248
249DEFINE_LIST(doubles, double)
250
251static void dot_polygon(agxbuf *sbuff, doubles_t xp, doubles_t yp,
252 double line_width, bool fill, const char *cstring) {
253
254 assert(doubles_size(&xp) == doubles_size(&yp));
255 if (!doubles_is_empty(&xp)){
256 if (fill) {
257 agxbprint(sbuff,
258 " c %" PRISIZE_T " -%s C %" PRISIZE_T " -%s P %" PRISIZE_T " ",
259 strlen(cstring), cstring, strlen(cstring), cstring,
260 doubles_size(&xp));
261 } else {
262 if (line_width > 0){
263 size_t len_swidth = (size_t)snprintf(NULL, 0, "%f", line_width);
264 agxbprint(sbuff, " c %" PRISIZE_T " -%s S %" PRISIZE_T
265 " -setlinewidth(%f) L %" PRISIZE_T " ", strlen(cstring), cstring,
266 len_swidth + 14, line_width, doubles_size(&xp));
267 } else {
268 agxbprint(sbuff, " c %" PRISIZE_T " -%s L %" PRISIZE_T " ", strlen(cstring),
269 cstring, doubles_size(&xp));
270 }
271 }
272 for (size_t i = 0; i < doubles_size(&xp); i++) {
273 agxbprint(sbuff, " %f %f", doubles_get(&xp, i), doubles_get(&yp, i));
274 }
275 }
276}
277
278static void plot_dot_polygons(agxbuf *sbuff, double line_width,
279 const char *line_color, SparseMatrix polys,
280 double *x_poly, int *polys_groups, float *r,
281 float *g, float *b, const char *opacity) {
282 int i, j, *ia = polys->ia, *ja = polys->ja, *a = polys->a, npolys = polys->m, nverts = polys->n, ipoly,first;
283 const bool fill = false;
284 const bool use_line = line_width >= 0;
285
286 agxbuf cstring_buffer = {0};
287 agxbput(&cstring_buffer, "#aaaaaaff");
288 char *cstring = agxbuse(&cstring_buffer);
289
290 doubles_t xp = {0};
291 doubles_t yp = {0};
292
293 if (Verbose) fprintf(stderr,"npolys = %d\n",npolys);
294 first = abs(a[0]); ipoly = first + 1;
295 for (i = 0; i < npolys; i++){
296 for (j = ia[i]; j < ia[i+1]; j++){
297 assert(ja[j] < nverts && ja[j] >= 0);
298 (void)nverts;
299 if (abs(a[j]) != ipoly){/* the first poly, or a hole */
300 ipoly = abs(a[j]);
301 if (r && g && b) {
302 rgb2hex(r[polys_groups[i]], g[polys_groups[i]], b[polys_groups[i]],
303 &cstring_buffer, opacity);
304 cstring = agxbuse(&cstring_buffer);
305 }
306 dot_polygon(sbuff, xp, yp, line_width, fill, cstring);
307 // start a new polygon
308 doubles_clear(&xp);
309 doubles_clear(&yp);
310 }
311 doubles_append(&xp, x_poly[2 * ja[j]]);
312 doubles_append(&yp, x_poly[2 * ja[j] + 1]);
313 }
314 if (use_line) {
315 dot_polygon(sbuff, xp, yp, line_width, fill, line_color);
316 } else {
317 /* why set fill to polys_groups[i]?*/
318 dot_polygon(sbuff, xp, yp, -1, true, cstring);
319 }
320 }
321 agxbfree(&cstring_buffer);
322 doubles_free(&xp);
323 doubles_free(&yp);
324}
325
326void plot_dot_map(Agraph_t* gr, int n, int dim, double *x, SparseMatrix polys,
327 SparseMatrix poly_lines, double line_width,
328 const char *line_color, double *x_poly, int *polys_groups,
329 char **labels, float *fsz, float *r, float *g, float *b,
330 const char* opacity, SparseMatrix A, FILE* f) {
331 /* if graph object exist, we just modify some attributes, otherwise we dump the whole graph */
332 bool plot_polyQ = true;
333 agxbuf sbuff = {0};
334
335 if (!r || !g || !b) plot_polyQ = false;
336
337 if (!gr) {
338 fprintf(f, "graph map {\n node [margin = 0 width=0.0001 height=0.00001 shape=plaintext];\n graph [outputorder=edgesfirst, bgcolor=\"#dae2ff\"]\n edge [color=\"#55555515\",fontname=\"Helvetica-Bold\"]\n");
339 } else {
340 agattr(gr, AGNODE, "margin", "0");
341 agattr(gr, AGNODE, "width", "0.0001");
342 agattr(gr, AGNODE, "height", "0.0001");
343 agattr(gr, AGNODE, "shape", "plaintext");
344 agattr(gr, AGNODE, "margin", "0");
345 agattr(gr, AGNODE, "fontname", "Helvetica-Bold");
346 agattr(gr, AGRAPH, "outputorder", "edgesfirst");
347 agattr(gr, AGRAPH, "bgcolor", "#dae2ff");
348 if (!A) agattr(gr, AGEDGE, "style","invis");/* do not plot edges */
349 }
350
351 /*polygons */
352 if (plot_polyQ) {
353 if (!gr) fprintf(f,"_background = \"");
354 plot_dot_polygons(&sbuff, -1., NULL, polys, x_poly, polys_groups, r, g, b, opacity);
355 }
356
357 /* polylines: line width is set here */
358 if (line_width >= 0){
359 plot_dot_polygons(&sbuff, line_width, line_color, poly_lines, x_poly, polys_groups, NULL, NULL, NULL, NULL);
360 }
361 if (!gr) {
362 fprintf(f,"%s",agxbuse(&sbuff));
363 fprintf(f,"\"\n");/* close polygons/lines */
364 } else {
365 agattr(gr, AGRAPH, "_background", agxbuse(&sbuff));
366 agwrite(gr, f);
367 }
368
369 /* nodes */
370 if (!gr && labels) plot_dot_labels(f, n, dim, x, labels, fsz);
371 /* edges */
372 if (!gr && A) plot_dot_edges(f, A);
373
374 /* background color + plot label?*/
375
376 if (!gr) fprintf(f, "}\n");
377
378 agxbfree(&sbuff);
379}
380
395static int get_tri(int n, int dim, double *x, int *nt, struct Triangle **T,
396 SparseMatrix *E) {
397 int i, j, i0, i1, i2, ntri;
399
400 int* trilist = get_triangles(x, n, &ntri);
401 if (trilist == NULL) {
402 return -1;
403 }
404
405 *T = gv_calloc(ntri, sizeof(struct Triangle));
406
408 for (i = 0; i < ntri; i++) {
409 for (j = 0; j < 3; j++) {
410 (*T)[i].vertices[j] = trilist[i * 3 + j];
411 }
412 i0 = (*T)[i].vertices[0]; i1 = (*T)[i].vertices[1]; i2 = (*T)[i].vertices[2];
413
414 triangle_center(&x[i0*dim], &x[i1*dim], &x[i2*dim], (*T)[i].center);
415 A = matrix_add_entry(A, i0, i1, i);
416 A = matrix_add_entry(A, i1, i2, i);
417 A = matrix_add_entry(A, i2, i0, i);
418 }
419
423 *E = B;
424
425 *nt = ntri;
426
427 free(trilist);
428 return 0;
429}
430
431static SparseMatrix get_country_graph(int n, SparseMatrix A, int *groups, int GRP_RANDOM, int GRP_BBOX){
432 /* form a graph each vertex is a group (a country), and a vertex is connected to another if the two countries shares borders.
433 since the group ID may not be contiguous (e.g., only groups 2,3,5, -1), we will return NULL if one of the group has non-positive ID! */
434 int *ia, *ja;
435 int one = 1, jj, i, j, ig1, ig2;
436 SparseMatrix B, BB;
437 int min_grp, max_grp;
438
439 min_grp = max_grp = groups[0];
440 for (i = 0; i < n; i++) {
441 max_grp = MAX(groups[i], max_grp);
442 min_grp = MIN(groups[i], min_grp);
443 }
444 if (min_grp <= 0) return NULL;
445 B = SparseMatrix_new(max_grp, max_grp, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
446 ia = A->ia;
447 ja = A->ja;
448 for (i = 0; i < n; i++){
449 ig1 = groups[i]-1;/* add a diagonal entry */
451 for (j = ia[i]; j < ia[i+1]; j++){
452 jj = ja[j];
453 if (i != jj && groups[i] != groups[jj] && groups[jj] != GRP_RANDOM && groups[jj] != GRP_BBOX){
454 ig1 = groups[i]-1; ig2 = groups[jj]-1;
456 }
457 }
458 }
461 return BB;
462}
463
464static void conn_comp(int n, SparseMatrix A, int *groups, SparseMatrix *poly_point_map){
465 /* form a graph where only vertices that are connected as well as in the same group are connected */
466 int *ia, *ja;
467 int one = 1, jj, i, j;
468 SparseMatrix B, BB;
469 int ncomps, *comps = NULL;
470
472 ia = A->ia;
473 ja = A->ja;
474 for (i = 0; i < n; i++){
475 for (j = ia[i]; j < ia[i+1]; j++){
476 jj = ja[j];
477 if (i != jj && groups[i] == groups[jj]){
479 }
480 }
481 }
483
484 int *comps_ptr = SparseMatrix_weakly_connected_components(BB, &ncomps, &comps);
487 *poly_point_map = SparseMatrix_new(ncomps, n, n, MATRIX_TYPE_PATTERN, FORMAT_CSR);
488 free((*poly_point_map)->ia);
489 free((*poly_point_map)->ja);
490 (*poly_point_map)->ia = comps_ptr;
491 (*poly_point_map)->ja = comps;
492 (*poly_point_map)->nz = n;
493
494}
495
496static void get_poly_lines(int nt, SparseMatrix E, int ncomps, int *comps_ptr,
497 int *comps, int *groups, SparseMatrix *poly_lines,
498 int **polys_groups, int GRP_RANDOM, int GRP_BBOX) {
499 /*============================================================
500
501 polygon outlines
502
503 ============================================================*/
504 int i, *tlist, nz, ipoly, nnt, ii, jj, t1, t2, t, cur, next, nn, j, nlink, sta;
505 int *elist, edim = 3;/* a list tell which vertex a particular vertex is linked with during poly construction.
506 since the surface is a cycle, each can only link with 2 others, the 3rd position is used to record how many links
507 */
508 int *ie = E->ia, *je = E->ja, *e = E->a;
510
511 int *mask = gv_calloc(nt, sizeof(int));
512 for (i = 0; i < nt; i++) mask[i] = -1;
513 /* loop over every point in each connected component */
514 elist = gv_calloc(nt * edim, sizeof(int));
515 tlist = gv_calloc(nt * 2, sizeof(int));
516 *poly_lines = SparseMatrix_new(ncomps, nt, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
517 *polys_groups = gv_calloc(ncomps, sizeof(int));
518
519 for (i = 0; i < nt; i++) elist[i*edim + 2] = 0;
520 nz = ie[E->m] - ie[0];
521
522 ipoly = 1;
523
524 for (i = 0; i < ncomps; i++){
525 nnt = 0;
526 for (j = comps_ptr[i]; j < comps_ptr[i+1]; j++){
527 ii = comps[j];
528
529 (*polys_groups)[i] = groups[ii];/* assign the grouping of each poly */
530
531 /* skip the country formed by random points */
532 if (groups[ii] == GRP_RANDOM || groups[ii] == GRP_BBOX) continue;
533
534 for (jj = ie[ii]; jj < ie[ii+1]; jj++){
535 if (groups[je[jj]] != groups[ii] && jj < nz - 1 && je[jj] == je[jj+1]){/* an triangle edge neighboring 2 triangles and two ends not in the same groups */
536 t1 = e[jj];
537 t2 = e[jj+1];
538
539 nlink = elist[t1*edim + 2]%2;
540 elist[t1*edim + nlink] = t2;/* t1->t2*/
541 elist[t1*edim + 2]++;
542
543 nlink = elist[t2*edim + 2]%2;
544 elist[t2*edim + nlink] = t1;/* t1->t2*/
545 elist[t2*edim + 2]++;
546
547 tlist[nnt++] = t1; tlist[nnt++] = t2;
548 jj++;
549 }
550 }
551 }/* done poly edges for this component i */
552
553 /* form one or more (if there is a hole) polygon outlines for this component */
554 for (j = 0; j < nnt; j++){
555 t = tlist[j];
556 if (mask[t] != i){
557 cur = sta = t; mask[cur] = i;
558 next = neighbor(t, 1, edim, elist);
559 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, cur, &ipoly);
560 while (next != sta){
561 mask[next] = i;
562
563 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, next, &ipoly);
564
565 nn = neighbor(next, 0, edim, elist);
566 if (nn == cur) {
567 nn = neighbor(next, 1, edim, elist);
568 }
569 assert(nn != cur);
570
571 cur = next;
572 next = nn;
573 }
574
575 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, sta, &ipoly);/* complete a cycle by adding starting point */
576
577 ipoly++;
578 }
579
580 }/* found poly_lines for this comp */
581 }
582
584 SparseMatrix_delete(*poly_lines);
585 *poly_lines = A;
586
587 free(tlist);
588 free(elist);
589 free(mask);
590}
591
592static void cycle_print(int head, int *cycle, int *edge_table){
593 int cur, next;
594
595 cur = head;
596 fprintf(stderr, "cycle (edges): {");
597 while ((next = cycle_next(cur)) != head){
598 fprintf(stderr, "%d,",cur);
599 cur = next;
600 }
601 fprintf(stderr, "%d}\n",cur);
602
603 cur = head;
604 fprintf(stderr, "cycle (vertices): ");
605 while ((next = cycle_next(cur)) != head){
606 fprintf(stderr, "%d--",edge_head(cur));
607 cur = next;
608 }
609 fprintf(stderr, "%d--%d\n",edge_head(cur),edge_tail(cur));
610}
611
612static int same_edge(int ecur, int elast, int *edge_table){
613 return (edge_head(ecur) == edge_head(elast) && edge_tail(ecur) == edge_tail(elast))
614 || (edge_head(ecur) == edge_tail(elast) && edge_tail(ecur) == edge_head(elast));
615}
616
617static void get_polygon_solids(int nt, SparseMatrix E, int ncomps,
618 int *comps_ptr, int *comps, SparseMatrix *polys)
619{
620 /*============================================================
621
622 polygon solids that will be colored
623
624 ============================================================*/
625 int *edge_table;/* a table of edges of the triangle graph. If two vertex u and v are connected and are adjacent to two triangles
626 t1 and t2, then from u there are two edges to v, one denoted as t1->t2, and the other t2->t1. They are
627 numbered as e1 and e2. edge_table[e1]={t1,t2} and edge_table[e2]={t2,t1}
628 */
629 SparseMatrix half_edges;/* a graph of triangle edges. If two vertex u and v are connected and are adjacent to two triangles
630 t1 and t2, then from u there are two edges to v, one denoted as t1->t2, and the other t2->t1. They are
631 numbered as e1 and e2. Likewise from v to u there are also two edges e1 and e2.
632 */
633
634 int n = E->m, *ie = E->ia, *je = E->ja, *e = E->a, ne, i, j, t1, t2, jj, ii;
635 int *cycle, cycle_head = 0;/* a list of edges that form a cycle that describe the polygon. cycle[e][0] gives the prev edge in the cycle from e,
636 cycle[e][1] gives the next edge
637 */
638 int *edge_cycle_map, NOT_ON_CYCLE = -1;/* map an edge e to its position on cycle, unless it does not exist (NOT_ON_CYCLE) */
639 int *emask;/* whether an edge is seen this iter */
640 enum {NO_DUPLICATE = -1};
641 int *elist, edim = 3;/* a list tell which edge a particular vertex is linked with when a voro cell has been visited,
642 since the surface is a cycle, each vertex can only link with 2 edges, the 3rd position is used to record how many links
643 */
644
645 int k, duplicate, ee = 0, ecur, enext, eprev, cur, next, nn, nlink, head, elast = 0, etail, tail, ehead, efirst;
646
647 int DEBUG_CYCLE = 0;
649
650 ne = E->nz;
651 edge_table = gv_calloc(ne * 2, sizeof(int));
652
653 half_edges = SparseMatrix_new(n, n, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
654
655 ne = 0;
656 for (i = 0; i < n; i++){
657 for (j = ie[i]; j < ie[i+1]; j++){
658 if (j < ie[n] - ie[0] - 1 && i > je[j] && je[j] == je[j+1]){/* an triangle edge neighboring 2 triangles. Since E is symmetric, we only do one edge of E*/
659 t1 = e[j];
660 t2 = e[j+1];
661 jj = je[j];
662 assert(jj < n);
663 edge_table[ne*2] = t1;/*t1->t2*/
664 edge_table[ne*2+1] = t2;
665 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, i, jj, &ne);
666 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, jj, i, &ne);
667 ne++;
668
669 edge_table[ne*2] = t2;/*t2->t1*/
670 edge_table[ne*2+1] = t1;
671 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, i, jj, &ne);
672 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, jj, i, &ne);
673
674
675 ne++;
676 j++;
677 }
678 }
679 }
680 assert(E->nz >= ne);
681
682 cycle = gv_calloc(ne * 2, sizeof(int));
684 SparseMatrix_delete(half_edges);half_edges = B;
685
686 edge_cycle_map = gv_calloc(ne, sizeof(int));
687 emask = gv_calloc(ne, sizeof(int));
688 for (i = 0; i < ne; i++) edge_cycle_map[i] = NOT_ON_CYCLE;
689 for (i = 0; i < ne; i++) emask[i] = -1;
690
691 ie = half_edges->ia;
692 je = half_edges->ja;
693 e = half_edges->a;
694 elist = gv_calloc(nt * 3, sizeof(int));
695 for (i = 0; i < nt; i++) elist[i*edim + 2] = 0;
696
697 *polys = SparseMatrix_new(ncomps, nt, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
698
699 for (i = 0; i < ncomps; i++){
700 if (DEBUG_CYCLE) fprintf(stderr, "\n ============ comp %d has %d members\n",i, comps_ptr[i+1]-comps_ptr[i]);
701 for (k = comps_ptr[i]; k < comps_ptr[i+1]; k++){
702 ii = comps[k];
703 duplicate = NO_DUPLICATE;
704 if (DEBUG_CYCLE) fprintf(stderr,"member = %d has %d neighbors\n",ii, ie[ii+1]-ie[ii]);
705 for (j = ie[ii]; j < ie[ii+1]; j++){
706 jj = je[j];
707 ee = e[j];
708 t1 = edge_head(ee);
709 if (DEBUG_CYCLE) fprintf(stderr," linked with %d using half-edge %d, {head,tail} of the edge = {%d, %d}\n",jj, ee, t1, edge_tail(ee));
710 nlink = elist[t1*edim + 2]%2;
711 elist[t1*edim + nlink] = ee;/* t1->t2*/
712 elist[t1*edim + 2]++;
713
714 if (edge_cycle_map[ee] != NOT_ON_CYCLE) duplicate = ee;
715 emask[ee] = ii;
716 }
717
718 if (duplicate == NO_DUPLICATE){
719 /* this must be the first time the cycle is being established, a new voro cell*/
720 ecur = ee;
721 cycle_head = ecur;
722 cycle_next(ecur) = ecur;
723 cycle_prev(ecur) = ecur;
724 edge_cycle_map[ecur] = 1;
725 head = cur = edge_head(ecur);
726 next = edge_tail(ecur);
727 if (DEBUG_CYCLE) fprintf(stderr, "NEW CYCLE\n starting with edge %d, {head,tail}={%d,%d}\n", ee, head, next);
728 while (next != head){
729 enext = neighbor(next, 0, edim, elist);/* two voro edges linked with triangle "next" */
730 if ((edge_head(enext) == cur && edge_tail(enext) == next)
731 || (edge_head(enext) == next && edge_tail(enext) == cur)){/* same edge */
732 enext = neighbor(next, 1, edim, elist);
733 };
734 if (DEBUG_CYCLE) fprintf(stderr, "cur edge = %d, next edge %d, {head,tail}={%d,%d},\n",ecur, enext, edge_head(enext), edge_tail(enext));
735 nn = edge_head(enext);
736 if (nn == next) nn = edge_tail(enext);
737 cycle_next(enext) = cycle_next(ecur);
738 cycle_prev(enext) = ecur;
739 cycle_next(ecur) = enext;
740 cycle_prev(ee) = enext;
741 edge_cycle_map[enext] = 1;
742
743 ecur = enext;
744 cur = next;
745 next = nn;
746 }
747 if (DEBUG_CYCLE) cycle_print(ee, cycle,edge_table);
748 } else {
749 /* we found a duplicate edge, remove that, and all contiguous neighbors that overlap with the current voro
750 */
751 ecur = ee = duplicate;
752 while (emask[ecur] == ii){
753 /* contiguous overlapping edges, Cycling is not possible
754 since the cycle can not complete surround the new voro cell and yet
755 do not contain any other edges
756 */
757 ecur = cycle_next(ecur);
758 }
759 if (DEBUG_CYCLE) fprintf(stderr," duplicating edge = %d, starting from the a non-duplicating edge %d, search backwards\n",ee, ecur);
760
761 ecur = cycle_prev(ecur);
762 efirst = ecur;
763 while (emask[ecur] == ii){
764 if (DEBUG_CYCLE) fprintf(stderr," remove edge %d (%d--%d)\n",ecur, edge_head(ecur), edge_tail(ecur));
765 /* short this duplicating edge */
766 edge_cycle_map[ecur] = NOT_ON_CYCLE;
767 enext = cycle_next(ecur);
768 eprev = cycle_prev(ecur);
769 cycle_next(ecur) = ecur;/* isolate this edge */
770 cycle_prev(ecur) = ecur;
771 cycle_next(eprev) = enext;/* short */
772 cycle_prev(enext) = eprev;
773 elast = ecur;/* record the last removed edge */
774 ecur = eprev;
775 }
776
777 if (DEBUG_CYCLE) {
778 fprintf(stderr, "remaining (broken) cycle = ");
779 cycle_print(cycle_next(ecur), cycle,edge_table);
780 }
781
782 /* we now have a broken cycle of head = edge_tail(ecur) and tail = edge_head(cycle_next(ecur)) */
783 ehead = ecur; etail = cycle_next(ecur);
784 cycle_head = ehead;
785 head = edge_tail(ehead);
786 tail = edge_head(etail);
787
788 /* pick an edge ev from head in the voro that is a removed edge: since the removed edges form a path starting from
789 efirst, and at elast (head of elast is head), usually we just need to check that ev is not the same as elast,
790 but in the case of a voro filling in a hole, we also need to check that ev is not efirst,
791 since in this case every edge of the voro cell is removed
792 */
793 ecur = neighbor(head, 0, edim, elist);
794 if (same_edge(ecur, elast, edge_table)){
795 ecur = neighbor(head, 1, edim, elist);
796 };
797
798 if (DEBUG_CYCLE) fprintf(stderr, "forwarding now from edge %d = {%d, %d}, try to reach vtx %d, first edge from voro = %d\n",
799 ehead, edge_head(ehead), edge_tail(ehead), tail, ecur);
800
801 /* now go along voro edges till we reach the tail of the broken cycle*/
802 cycle_next(ehead) = ecur;
803 cycle_prev(ecur) = ehead;
804 cycle_prev(etail) = ecur;
805 cycle_next(ecur) = etail;
806 if (same_edge(ecur, efirst, edge_table)){
807 if (DEBUG_CYCLE) fprintf(stderr, "this voro cell fill in a hole completely!!!!\n");
808 } else {
809
810 edge_cycle_map[ecur] = 1;
811 head = cur = edge_head(ecur);
812 next = edge_tail(ecur);
813 if (DEBUG_CYCLE) fprintf(stderr, "starting with edge %d, {head,tail}={%d,%d}\n", ecur, head, next);
814 while (next != tail){
815 enext = neighbor(next, 0, edim, elist);/* two voro edges linked with triangle "next" */
816 if ((edge_head(enext) == cur && edge_tail(enext) == next)
817 || (edge_head(enext) == next && edge_tail(enext) == cur)){/* same edge */
818 enext = neighbor(next, 1, edim, elist);
819 };
820 if (DEBUG_CYCLE) fprintf(stderr, "cur edge = %d, next edge %d, {head,tail}={%d,%d},\n",ecur, enext, edge_head(enext), edge_tail(enext));
821
822
823 nn = edge_head(enext);
824 if (nn == next) nn = edge_tail(enext);
825 cycle_next(enext) = cycle_next(ecur);
826 cycle_prev(enext) = ecur;
827 cycle_next(ecur) = enext;
828 cycle_prev(etail) = enext;
829 edge_cycle_map[enext] = 1;
830
831 ecur = enext;
832 cur = next;
833 next = nn;
834 }
835 }
836
837 }
838
839 }
840 /* done this component, load to sparse matrix, unset edge_map*/
841 ecur = cycle_head;
842 while ((enext = cycle_next(ecur)) != cycle_head){
843 edge_cycle_map[ecur] = NOT_ON_CYCLE;
844 head = edge_head(ecur);
846 ecur = enext;
847 }
848 edge_cycle_map[ecur] = NOT_ON_CYCLE;
849 head = edge_head(ecur); tail = edge_tail(ecur);
851 SparseMatrix_coordinate_form_add_entry(*polys, i, tail, &i);
852
853
854 /* unset edge_map */
855 }
856
858 SparseMatrix_delete(*polys);
859 *polys = B;
860
861 SparseMatrix_delete(half_edges);
862 free(cycle);
863 free(edge_cycle_map);
864 free(elist);
865 free(emask);
866 free(edge_table);
867}
868
869static void get_polygons(int n, int nrandom, int dim, int *grouping, int nt,
870 struct Triangle *Tp, SparseMatrix E, int *nverts,
871 double **x_poly, SparseMatrix *poly_lines,
872 SparseMatrix *polys, int **polys_groups,
873 SparseMatrix *poly_point_map,
874 SparseMatrix *country_graph) {
875 int i, j;
876 int *groups;
877 int maxgrp;
878 int *comps = NULL, *comps_ptr = NULL, ncomps;
879 int GRP_RANDOM, GRP_BBOX;
880
881 assert(dim == 2);
882 *nverts = nt;
883
884 groups = gv_calloc(n + nrandom, sizeof(int));
885 maxgrp = grouping[0];
886 for (i = 0; i < n; i++) {
887 maxgrp = MAX(maxgrp, grouping[i]);
888 groups[i] = grouping[i];
889 }
890
891 GRP_RANDOM = maxgrp + 1; GRP_BBOX = maxgrp + 2;
892 for (i = n; i < n + nrandom - 4; i++) {/* all random points in the same group */
893 groups[i] = GRP_RANDOM;
894 }
895 for (i = n + nrandom - 4; i < n + nrandom; i++) {/* last 4 pts of the expanded bonding box in the same group */
896 groups[i] = GRP_BBOX;
897 }
898
899 /* finding connected components: vertices that are connected in the triangle graph, as well as in the same group */
900 conn_comp(n + nrandom, E, groups, poly_point_map);
901
902 ncomps = (*poly_point_map)->m;
903 comps = (*poly_point_map)->ja;
904 comps_ptr = (*poly_point_map)->ia;
905
906 /* connected components are such that the random points and the bounding box 4 points forms the last
907 remaining components */
908 for (i = ncomps - 1; i >= 0; i--) {
909 if (groups[comps[comps_ptr[i]]] != GRP_RANDOM &&
910 groups[comps[comps_ptr[i]]] != GRP_BBOX) break;
911 }
912 ncomps = i + 1;
913 if (Verbose) fprintf(stderr,"ncomps = %d\n",ncomps);
914
915 *x_poly = gv_calloc(dim * nt, sizeof(double));
916 for (i = 0; i < nt; i++){
917 for (j = 0; j < dim; j++){
918 (*x_poly)[i*dim+j] = Tp[i].center[j];
919 }
920 }
921
922 /*============================================================
923
924 polygon outlines
925
926 ============================================================*/
927 get_poly_lines(nt, E, ncomps, comps_ptr, comps, groups, poly_lines,
928 polys_groups, GRP_RANDOM, GRP_BBOX);
929
930 /*============================================================
931
932 polygon solids
933
934 ============================================================*/
935 get_polygon_solids(nt, E, ncomps, comps_ptr, comps, polys);
936
937 *country_graph = get_country_graph(n, E, groups, GRP_RANDOM, GRP_BBOX);
938
939 free(groups);
940}
941
942static int make_map_internal(bool include_OK_points, int n, int dim, double *x0,
943 int *grouping0, SparseMatrix graph,
944 double bounding_box_margin, int nrandom,
945 int nedgep, double shore_depth_tol, int *nverts,
946 double **x_poly, SparseMatrix *poly_lines,
947 SparseMatrix *polys, int **polys_groups,
948 SparseMatrix *poly_point_map,
949 SparseMatrix *country_graph, int highlight_cluster) {
950
951
952 double xmax[2], xmin[2], area, *x = x0;
953 int i, j;
954 QuadTree qt;
955 int dim2 = 2, nn = 0;
956 int max_qtree_level = 10;
957 double ymin[2], min;
958 int imin, nzok = 0, nzok0 = 0, nt;
959 double *xran, point[2];
960 struct Triangle *Tp;
962 double boxsize[2];
963 bool INCLUDE_OK_POINTS = include_OK_points;/* OK points are random points inserted and found to be within shore_depth_tol of real/artificial points,
964 including them instead of throwing away increase realism of boundary */
965 int *grouping = grouping0;
966
967 int HIGHLIGHT_SET = highlight_cluster;
968
969 for (j = 0; j < dim2; j++) {
970 xmax[j] = x[j];
971 xmin[j] = x[j];
972 }
973
974 for (i = 0; i < n; i++){
975 for (j = 0; j < dim2; j++) {
976 xmax[j] = fmax(xmax[j], x[i*dim+j]);
977 xmin[j] = fmin(xmin[j], x[i*dim+j]);
978 }
979 }
980 boxsize[0] = xmax[0] - xmin[0];
981 boxsize[1] = xmax[1] - xmin[1];
982 area = boxsize[0]*boxsize[1];
983
984 if (nrandom == 0) {
985 nrandom = n;
986 } else if (nrandom < 0){
987 nrandom = -nrandom * n;
988 } else if (nrandom < 4) {/* by default we add 4 point on 4 corners anyway */
989 nrandom = 0;
990 } else {
991 nrandom -= 4;
992 }
993
994 if (shore_depth_tol < 0) shore_depth_tol = sqrt(area/(double) n); /* set to average distance for random distribution */
995 if (Verbose) fprintf(stderr, "nrandom=%d shore_depth_tol=%.08f\n", nrandom, shore_depth_tol);
996
997
998 /* add artificial points along each edge to avoid as much as possible
999 two connected components be separated due to small shore depth */
1000 {
1001 int nz;
1002 double *y;
1003 int k, t, np=nedgep;
1004 if (graph && np){
1005 fprintf(stderr,"add art np = %d\n",np);
1006 nz = graph->nz;
1007 y = gv_calloc(dim * n + dim * nz * np, sizeof(double));
1008 for (i = 0; i < n*dim; i++) y[i] = x[i];
1009 grouping = gv_calloc(n + nz * np, sizeof(int));
1010 for (i = 0; i < n; i++) grouping[i] = grouping0[i];
1011 nz = n;
1012 for (i = 0; i < graph->m; i++){
1013
1014 for (j = graph->ia[i]; j < graph->ia[i+1]; j++){
1015 if (!HIGHLIGHT_SET || (grouping[i] == grouping[graph->ja[j]] && grouping[i] == HIGHLIGHT_SET)){
1016 for (t = 0; t < np; t++){
1017 for (k = 0; k < dim; k++){
1018 y[nz*dim+k] = t/((double) np)*x[i*dim+k] + (1-t/((double) np))*x[(graph->ja[j])*dim + k];
1019 }
1020 assert(n + (nz-n)*np + t < n + nz*np && n + (nz-n)*np + t >= 0);
1021 if (t/((double) np) > 0.5){
1022 grouping[nz] = grouping[i];
1023 } else {
1024 grouping[nz] = grouping[graph->ja[j]];
1025 }
1026 nz++;
1027 }
1028 }
1029 }
1030 }
1031 fprintf(stderr, "after adding edge points, n:%d->%d\n",n, nz);
1032 n = nz;
1033 x = y;
1034 qt = QuadTree_new_from_point_list(dim, nz, max_qtree_level, y);
1035 } else {
1036 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
1037 }
1038 }
1039
1040 /* generate random points for lake/sea effect */
1041 if (nrandom != 0){
1042 for (i = 0; i < dim2; i++) {
1043 if (bounding_box_margin > 0){
1044 xmin[i] -= bounding_box_margin;
1045 xmax[i] += bounding_box_margin;
1046 } else if (bounding_box_margin < 0) {
1047 xmin[i] -= boxsize[i]*(-bounding_box_margin);
1048 xmax[i] += boxsize[i]*(-bounding_box_margin);
1049 } else { // auto bounding box
1050 xmin[i] -= fmax(boxsize[i] * 0.2, 2.* shore_depth_tol);
1051 xmax[i] += fmax(boxsize[i] * 0.2, 2 * shore_depth_tol);
1052 }
1053 }
1054 if (Verbose) {
1055 double bbm = bounding_box_margin;
1056 if (bbm > 0)
1057 fprintf (stderr, "bounding box margin: %.06f", bbm);
1058 else if (bbm < 0)
1059 fprintf (stderr, "bounding box margin: (%.06f * %.06f)", boxsize[0], -bbm);
1060 else
1061 fprintf(stderr, "bounding box margin: %.06f",
1062 fmax(boxsize[0] * 0.2, 2 * shore_depth_tol));
1063 }
1064 if (nrandom < 0) {
1065 const double area2 = (xmax[1] - xmin[1]) * (xmax[0] - xmin[0]);
1066 const double n1 = floor(area2 / (shore_depth_tol * shore_depth_tol));
1067 const double n2 = n * floor(area2 / area);
1068 nrandom = fmax(n1, n2);
1069 }
1070 srand(123);
1071 xran = gv_calloc((nrandom + 4) * dim2, sizeof(double));
1072 int nz = 0;
1073 if (INCLUDE_OK_POINTS){
1074 nzok0 = nzok = nrandom - 1;/* points that are within tolerance of real or artificial points */
1075 if (grouping == grouping0) {
1076 int *grouping2 = gv_calloc(n + nrandom, sizeof(int));
1077 memcpy(grouping2, grouping, sizeof(int)*n);
1078 grouping = grouping2;
1079 } else {
1080 grouping = gv_recalloc(grouping, n, n + nrandom, sizeof(int));
1081 }
1082 }
1083 nn = n;
1084
1085 for (i = 0; i < nrandom; i++){
1086
1087 for (j = 0; j < dim2; j++){
1088 point[j] = xmin[j] + (xmax[j] - xmin[j])*drand();
1089 }
1090
1091 QuadTree_get_nearest(qt, point, ymin, &imin, &min);
1092
1093 if (min > shore_depth_tol){/* point not too close, accepted */
1094 for (j = 0; j < dim2; j++){
1095 xran[nz*dim2+j] = point[j];
1096 }
1097 nz++;
1098 } else if (INCLUDE_OK_POINTS && min > shore_depth_tol/10){/* avoid duplicate points */
1099 for (j = 0; j < dim2; j++){
1100 xran[nzok*dim2+j] = point[j];
1101 }
1102 grouping[nn++] = grouping[imin];
1103 nzok--;
1104
1105 }
1106
1107 }
1108 nrandom = nz;
1109 if (Verbose) fprintf(stderr, "nn nrandom=%d\n", nrandom);
1110 } else {
1111 xran = gv_calloc(4 * dim2, sizeof(double));
1112 }
1113
1114
1115
1116 /* add 4 corners even if nrandom = 0. The corners should be further away from the other points to avoid skinny triangles */
1117 for (i = 0; i < dim2; i++) xmin[i] -= 0.2*(xmax[i]-xmin[i]);
1118 for (i = 0; i < dim2; i++) xmax[i] += 0.2*(xmax[i]-xmin[i]);
1119 i = nrandom;
1120 for (j = 0; j < dim2; j++) xran[i*dim2+j] = xmin[j];
1121 i++;
1122 for (j = 0; j < dim2; j++) xran[i*dim2+j] = xmax[j];
1123 i++;
1124 xran[i*dim2] = xmin[0]; xran[i*dim2+1] = xmax[1];
1125 i++;
1126 xran[i*dim2] = xmax[0]; xran[i*dim2+1] = xmin[1];
1127 nrandom += 4;
1128
1129
1130 double *xcombined;
1131 if (INCLUDE_OK_POINTS){
1132 xcombined = gv_calloc((nn + nrandom) * dim2, sizeof(double));
1133 } else {
1134 xcombined = gv_calloc((n + nrandom) * dim2, sizeof(double));
1135 }
1136 for (i = 0; i < n; i++) {
1137 for (j = 0; j < dim2; j++) xcombined[i*dim2+j] = x[i*dim+j];
1138 }
1139 for (i = 0; i < nrandom; i++) {
1140 for (j = 0; j < dim2; j++) xcombined[(i + nn)*dim2+j] = xran[i*dim+j];
1141 }
1142
1143 if (INCLUDE_OK_POINTS){
1144 for (i = 0; i < nn - n; i++) {
1145 for (j = 0; j < dim2; j++) xcombined[(i + n)*dim2+j] = xran[(nzok0 - i)*dim+j];
1146 }
1147 n = nn;
1148 }
1149
1150
1151 {
1152 int nz, nh = 0;/* the set to highlight */
1153 if (HIGHLIGHT_SET){
1154 if (Verbose) fprintf(stderr," highlight cluster %d, n = %d\n",HIGHLIGHT_SET, n);
1155 /* shift set to the beginning */
1156 nz = 0;
1157 for (i = 0; i < n; i++){
1158 if (grouping[i] == HIGHLIGHT_SET){
1159 nh++;
1160 for (j = 0; j < dim; j++){
1161 xcombined[nz++] = x[i*dim+j];
1162 }
1163 }
1164 }
1165 for (i = 0; i < n; i++){
1166 if (grouping[i] != HIGHLIGHT_SET){
1167 for (j = 0; j < dim; j++){
1168 xcombined[nz++] = x[i*dim+j];
1169 }
1170 }
1171 }
1172 assert(nz == n*dim);
1173 for (i = 0; i < nh; i++){
1174 grouping[i] = 1;
1175 }
1176 for (i = nh; i < n; i++){
1177 grouping[i] = 2;
1178 }
1179 nrandom += n - nh;/* count everything except cluster HIGHLIGHT_SET as random */
1180 n = nh;
1181 if (Verbose) fprintf(stderr,"nh = %d\n",nh);
1182 }
1183 }
1184
1185 int rc = 0;
1186 if (get_tri(n + nrandom, dim2, xcombined, &nt, &Tp, &E) != 0) {
1187 rc = -1;
1188 goto done;
1189 }
1190 get_polygons(n, nrandom, dim2, grouping, nt, Tp, E, nverts, x_poly,
1191 poly_lines, polys, polys_groups, poly_point_map, country_graph);
1192
1194 free(Tp);
1195done:
1196 free(xcombined);
1197 free(xran);
1198 if (grouping != grouping0) free(grouping);
1199 if (x != x0) free(x);
1200 return rc;
1201}
1202
1203static void add_point(int *n, int igrp, double **x, int *nmax, double point[], int **groups){
1204
1205 if (*n >= *nmax){
1206 int old_nmax = *nmax;
1207 *nmax = 20 + *n;
1208 *x = gv_recalloc(*x, 2 * old_nmax, 2 * *nmax, sizeof(double));
1209 *groups = gv_recalloc(*groups, old_nmax, *nmax, sizeof(int));
1210 }
1211
1212 (*x)[(*n)*2] = point[0];
1213 (*x)[(*n)*2+1] = point[1];
1214 (*groups)[*n] = igrp;
1215 (*n)++;
1216}
1217
1218static void get_boundingbox(int n, int dim, double *x, double *width, double *bbox){
1219 int i;
1220 bbox[0] = bbox[1] = x[0];
1221 bbox[2] = bbox[3] = x[1];
1222
1223 for (i = 0; i < n; i++){
1224 bbox[0] = fmin(bbox[0], x[i * dim] - width[i * dim]);
1225 bbox[1] = fmax(bbox[1], x[i * dim] + width[i * dim]);
1226 bbox[2] = fmin(bbox[2], x[i * dim + 1] - width[i * dim + 1]);
1227 bbox[3] = fmax(bbox[3], x[i * dim + 1] + width[i * dim + 1]);
1228 }
1229}
1230
1231int make_map_from_rectangle_groups(bool include_OK_points,
1232 int n, int dim, double *x, double *sizes,
1233 int *grouping, SparseMatrix graph, double bounding_box_margin, int nrandom, int *nart, int nedgep,
1234 double shore_depth_tol,
1235 int *nverts, double **x_poly,
1236 SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map,
1237 SparseMatrix *country_graph, int highlight_cluster){
1238
1239 /* create a list of polygons from a list of rectangles in 2D. rectangles belong to groups. rectangles in the same group that are also close
1240 geometrically will be in the same polygon describing the outline of the group. The main difference for this function and
1241 make_map_from_point_groups is that in this function, the input are points with width/heights, and we try not to place
1242 "lakes" inside these rectangles. This is achieved approximately by adding artificial points along the perimeter of the rectangles,
1243 as well as near the center.
1244
1245 input:
1246 include_OK_points: OK points are random points inserted and found to be within shore_depth_tol of real/artificial points,
1247 . including them instead of throwing away increase realism of boundary
1248 n: number of points
1249 dim: dimension of the points. If dim > 2, only the first 2D is used.
1250 x: coordinates
1251 sizes: width and height
1252 grouping: which group each of the vertex belongs to
1253 graph: the link structure between points. If graph == NULL, this is not used. otherwise
1254 . it is assumed that matrix is symmetric and the graph is undirected
1255 bounding_box_margin: margin used to form the bounding box.
1256 . if negative, it is taken as relative. i.e., -0.5 means a margin of 0.5*box_size
1257 nrandom (input): number of random points to insert in the bounding box to figure out lakes and seas.
1258 . If nrandom = 0, no points are inserted, if nrandom < 0, the number is decided automatically.
1259 .
1260 nart: on entry, number of artificial points to be added along each side of a rectangle enclosing the labels. if < 0, auto-selected.
1261 . On exit, actual number of artificial points added.
1262 nedgep: number of artificial points are adding along edges to establish as much as possible a bright between nodes
1263 . connected by the edge, and avoid islands that are connected. k = 0 mean no points.
1264 shore_depth_tol: nrandom random points are inserted in the bounding box of the points,
1265 . such random points are then weeded out if it is within distance of shore_depth_tol from
1266 . real points. If 0, auto assigned
1267
1268 output:
1269 nverts: number of vertices in the Voronoi diagram
1270 x_poly: the 2D coordinates of these polygons, dimension nverts*2
1271 poly_lines: the sparse matrix representation of the polygon indices, as well as their identity. The matrix is of size
1272 . npolygons x nverts. The i-th polygon is formed by linking vertices with index in the i-th row of the sparse matrix.
1273 . Each row is of the form {{i,j1,m},...{i,jk,m},{i,j1,m},{i,l1,m+1},...}, where j1--j2--jk--j1 form one loop,
1274 . and l1 -- l2 -- ... form another. Each row can have more than 1 loop only when the connected region the polylines represent
1275 . has at least 1 holes.
1276 polys: the sparse matrix representation of the polygon indices, as well as their identity. The matrix is of size
1277 . npolygons x nverts. The i-th polygon is formed by linking vertices with index in the i-th row of the sparse matrix.
1278 . Unlike poly_lines, here each row represent an one stroke drawing of the SOLID polygon, vertices
1279 . along this path may repeat
1280 polys_groups: the group (color) each polygon belongs to, this include all groups of the real points,
1281 . plus the random point group and the bounding box group
1282 poly_point_map: a matrix of dimension npolys x (n + nrandom), poly_point_map[i,j] != 0 if polygon i contains the point j.
1283 . If j < n, it is the original point, otherwise it is artificial point (forming the rectangle around a label) or random points.
1284 country_graph: shows which country is a neighbor of which country.
1285 . if country i and country j are neighbor, then the {i,j} entry is the total number of vertices that
1286 . belongs to i and j, and share an edge of the triangulation. In addition, {i,i} and {j,j} have values equal
1287 . to the number of vertices in each of the countries. If the input "grouping" has negative or zero value, then
1288 . country_graph = NULL.
1289
1290
1291 */
1292 double *X;
1293 int N, nmax, i, j, k, igrp;
1294 int *groups, K = *nart;/* average number of points added per side of rectangle */
1295
1296 double avgsize[2], avgsz, h[2], p1, p0;
1297 double point[2];
1298 int nadded[2];
1299 double delta[2];
1300 double bbox[4];
1301
1302 if (K < 0){
1303 K = (int) 10/(1+n/400.);/* 0 if n > 3600*/
1304 }
1305 *nart = 0;
1306 if (Verbose){
1307 int maxgp = grouping[0];
1308 int mingp = grouping[0];
1309 for (i = 0; i < n; i++) {
1310 maxgp = MAX(maxgp, grouping[i]);
1311 mingp = MIN(mingp, grouping[i]);
1312 }
1313 fprintf(stderr, "max grouping - min grouping + 1 = %d\n",maxgp - mingp + 1);
1314 }
1315
1316 int rc = 0;
1317 if (!sizes){
1318 return make_map_internal(include_OK_points, n, dim, x, grouping, graph,
1319 bounding_box_margin, nrandom, nedgep,
1320 shore_depth_tol, nverts, x_poly, poly_lines, polys,
1321 polys_groups, poly_point_map, country_graph,
1322 highlight_cluster);
1323 } else {
1324
1325 /* add artificial node due to node sizes */
1326 avgsize[0] = 0;
1327 avgsize[1] = 0;
1328 for (i = 0; i < n; i++){
1329 for (j = 0; j < 2; j++) {
1330 avgsize[j] += sizes[i*dim+j];
1331 }
1332 }
1333 for (i = 0; i < 2; i++) avgsize[i] /= n;
1334 avgsz = 0.5*(avgsize[0] + avgsize[1]);
1335 if (Verbose) fprintf(stderr, "avgsize = {%f, %f}\n",avgsize[0], avgsize[1]);
1336
1337 nmax = 2*n;
1338 X = gv_calloc(dim * (n + nmax), sizeof(double));
1339 groups = gv_calloc(n + nmax, sizeof(int));
1340 for (i = 0; i < n; i++) {
1341 groups[i] = grouping[i];
1342 for (j = 0; j < 2; j++){
1343 X[i*2+j] = x[i*dim+j];
1344 }
1345 }
1346 N = n;
1347
1348 if (shore_depth_tol < 0) {
1349 shore_depth_tol = -(shore_depth_tol)*avgsz;
1350 } else if (shore_depth_tol == 0){
1351 get_boundingbox(n, dim, x, sizes, bbox);
1352 const double area = (bbox[1] - bbox[0]) * (bbox[3] - bbox[2]);
1353 shore_depth_tol = sqrt(area / n);
1354 if (Verbose) fprintf(stderr,"setting shore length ======%f\n",shore_depth_tol);
1355 } else {
1356 }
1357
1358 /* add artificial points in an anti-clockwise fashion */
1359
1360 if (K > 0){
1361 delta[0] = .5*avgsize[0]/K; delta[1] = .5*avgsize[1]/K;/* small perturbation to make boundary between labels looks more fractal */
1362 } else {
1363 delta[0] = delta[1] = 0.;
1364 }
1365 for (i = 0; i < n; i++){
1366 igrp = grouping[i];
1367 for (j = 0; j < 2; j++) {
1368 if (avgsz == 0){
1369 nadded[j] = 0;
1370 } else {
1371 nadded[j] = (int) K*sizes[i*dim+j]/avgsz;
1372 }
1373 }
1374
1375 /*top: left to right */
1376 if (nadded[0] > 0){
1377 h[0] = sizes[i*dim]/nadded[0];
1378 point[0] = x[i*dim] - sizes[i*dim]/2;
1379 p1 = point[1] = x[i*dim+1] + sizes[i*dim + 1]/2;
1380 add_point(&N, igrp, &X, &nmax, point, &groups);
1381 for (k = 0; k < nadded[0] - 1; k++){
1382 point[0] += h[0];
1383 point[1] = p1 + (0.5-drand())*delta[1];
1384 add_point(&N, igrp, &X, &nmax, point, &groups);
1385 }
1386
1387 /* bot: right to left */
1388 point[0] = x[i*dim] + sizes[i*dim]/2;
1389 p1 = point[1] = x[i*dim+1] - sizes[i*dim + 1]/2;
1390 add_point(&N, igrp, &X, &nmax, point, &groups);
1391 for (k = 0; k < nadded[0] - 1; k++){
1392 point[0] -= h[0];
1393 point[1] = p1 + (0.5-drand())*delta[1];
1394 add_point(&N, igrp, &X, &nmax, point, &groups);
1395 }
1396 }
1397
1398 if (nadded[1] > 0){
1399 /* left: bot to top */
1400 h[1] = sizes[i*dim + 1]/nadded[1];
1401 p0 = point[0] = x[i*dim] - sizes[i*dim]/2;
1402 point[1] = x[i*dim+1] - sizes[i*dim + 1]/2;
1403 add_point(&N, igrp, &X, &nmax, point, &groups);
1404 for (k = 0; k < nadded[1] - 1; k++){
1405 point[0] = p0 + (0.5-drand())*delta[0];
1406 point[1] += h[1];
1407 add_point(&N, igrp, &X, &nmax, point, &groups);
1408 }
1409
1410 /* right: top to bot */
1411 p0 = point[0] = x[i*dim] + sizes[i*dim]/2;
1412 point[1] = x[i*dim+1] + sizes[i*dim + 1]/2;
1413 add_point(&N, igrp, &X, &nmax, point, &groups);
1414 for (k = 0; k < nadded[1] - 1; k++){
1415 point[0] = p0 + (0.5-drand())*delta[0];
1416 point[1] -= h[1];
1417 add_point(&N, igrp, &X, &nmax, point, &groups);
1418 }
1419 }
1420 *nart = N - n;
1421
1422 }/* done adding artificial points due to node size*/
1423
1424 rc = make_map_internal(include_OK_points, N, dim, X, groups, graph,
1425 bounding_box_margin, nrandom, nedgep,
1426 shore_depth_tol, nverts, x_poly, poly_lines, polys,
1427 polys_groups, poly_point_map, country_graph,
1428 highlight_cluster);
1429 free(groups);
1430 free(X);
1431 }
1432 return rc;
1433}
void QuadTree_get_nearest(QuadTree qt, double *x, double *ymin, int *imin, double *min)
Definition QuadTree.c:682
QuadTree QuadTree_new_from_point_list(int dim, int n, int max_level, double *coord)
Definition QuadTree.c:309
void SparseMatrix_distance_matrix(SparseMatrix D0, double **dist0)
SparseMatrix SparseMatrix_from_coordinate_format(SparseMatrix A)
SparseMatrix SparseMatrix_transpose(SparseMatrix A)
SparseMatrix SparseMatrix_symmetrize(SparseMatrix A, bool pattern_symmetric_only)
SparseMatrix SparseMatrix_coordinate_form_add_entry(SparseMatrix A, int irn, int jcn, const void *val)
void SparseMatrix_export(FILE *f, SparseMatrix A)
void SparseMatrix_delete(SparseMatrix A)
SparseMatrix SparseMatrix_get_real_adjacency_matrix_symmetrized(SparseMatrix A)
SparseMatrix SparseMatrix_sort(SparseMatrix A)
int * SparseMatrix_weakly_connected_components(SparseMatrix A0, int *ncomp, int **comps)
SparseMatrix SparseMatrix_from_coordinate_format_not_compacted(SparseMatrix A)
SparseMatrix SparseMatrix_remove_diagonal(SparseMatrix A)
SparseMatrix SparseMatrix_new(int m, int n, int nz, int type, int format)
SparseMatrix SparseMatrix_from_dense(int m, int n, double *x)
@ MATRIX_TYPE_PATTERN
@ MATRIX_TYPE_INTEGER
@ FORMAT_COORD
@ FORMAT_CSR
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define MIN(a, b)
Definition arith.h:28
#define N(n)
Definition bcomps.c:58
abstract graph C library, Cgraph API
void rgb2hex(float r, float g, float b, agxbuf *cstring, const char *opacity)
Definition colorutil.c:21
void country_graph_coloring(int seed, SparseMatrix A, int **p)
int * get_triangles(double *x, int n, int *tris)
Definition delaunay.c:709
#define head
Definition dthdr.h:15
static long seed
Definition exeval.c:1035
#define A(n, t)
Definition expr.h:76
static double dist(int dim, double *x, double *y)
#define E
Definition gdefs.h:6
#define X(prefix, name, str, type, subtype,...)
Definition gdefs.h:14
void vector_float_take(int n, float *v, int m, int *p, float **u)
Definition general.c:78
double distance_cropped(double *x, int dim, int i, int j)
Definition general.c:140
double xmax
Definition geometry.c:15
double ymin
Definition geometry.c:15
double xmin
Definition geometry.c:15
static bool Verbose
Definition gml2gv.c:23
void free(void *)
node NULL
Definition grammar.y:163
static double drand(void)
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:338
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:730
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
Agraph_t * graph(char *name)
Definition gv.cpp:30
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:27
agxbput(xb, staging)
static int z
#define B
Definition hierarchy.c:117
#define D
Definition hierarchy.c:119
#define DEFINE_LIST(name, type)
Definition list.h:26
static int make_map_internal(bool include_OK_points, int n, int dim, double *x0, int *grouping0, SparseMatrix graph, double bounding_box_margin, int nrandom, int nedgep, double shore_depth_tol, int *nverts, double **x_poly, SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map, SparseMatrix *country_graph, int highlight_cluster)
Definition make_map.c:942
static void triangle_center(double x[], double y[], double z[], double c[])
Definition make_map.c:176
void map_palette_optimal_coloring(char *color_scheme, SparseMatrix A0, float **rgb_r, float **rgb_g, float **rgb_b)
Definition make_map.c:34
static void plot_dot_edges(FILE *f, SparseMatrix A)
Definition make_map.c:221
static void get_polygons(int n, int nrandom, int dim, int *grouping, int nt, struct Triangle *Tp, SparseMatrix E, int *nverts, double **x_poly, SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map, SparseMatrix *country_graph)
Definition make_map.c:869
static void add_point(int *n, int igrp, double **x, int *nmax, double point[], int **groups)
Definition make_map.c:1203
static SparseMatrix get_country_graph(int n, SparseMatrix A, int *groups, int GRP_RANDOM, int GRP_BBOX)
Definition make_map.c:431
static int same_edge(int ecur, int elast, int *edge_table)
Definition make_map.c:612
static void get_boundingbox(int n, int dim, double *x, double *width, double *bbox)
Definition make_map.c:1218
int make_map_from_rectangle_groups(bool include_OK_points, int n, int dim, double *x, double *sizes, int *grouping, SparseMatrix graph, double bounding_box_margin, int nrandom, int *nart, int nedgep, double shore_depth_tol, int *nverts, double **x_poly, SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map, SparseMatrix *country_graph, int highlight_cluster)
Definition make_map.c:1231
static void get_poly_lines(int nt, SparseMatrix E, int ncomps, int *comps_ptr, int *comps, int *groups, SparseMatrix *poly_lines, int **polys_groups, int GRP_RANDOM, int GRP_BBOX)
Definition make_map.c:496
static int get_tri(int n, int dim, double *x, int *nt, struct Triangle **T, SparseMatrix *E)
Definition make_map.c:395
static SparseMatrix matrix_add_entry(SparseMatrix A, int i, int j, int val)
Definition make_map.c:212
static void normal(double v[], double normal[])
Definition make_map.c:167
static void cycle_print(int head, int *cycle, int *edge_table)
Definition make_map.c:592
void map_optimal_coloring(int seed, SparseMatrix A, float *rgb_r, float *rgb_g, float *rgb_b)
Definition make_map.c:93
static void plot_dot_labels(FILE *f, int n, int dim, double *x, char **labels, float *fsz)
Definition make_map.c:236
static void dot_polygon(agxbuf *sbuff, doubles_t xp, doubles_t yp, double line_width, bool fill, const char *cstring)
Definition make_map.c:251
static int get_poly_id(int ip, SparseMatrix point_poly_map)
Definition make_map.c:111
static void get_polygon_solids(int nt, SparseMatrix E, int ncomps, int *comps_ptr, int *comps, SparseMatrix *polys)
Definition make_map.c:617
static void plot_dot_polygons(agxbuf *sbuff, double line_width, const char *line_color, SparseMatrix polys, double *x_poly, int *polys_groups, float *r, float *g, float *b, const char *opacity)
Definition make_map.c:278
void plot_dot_map(Agraph_t *gr, int n, int dim, double *x, SparseMatrix polys, SparseMatrix poly_lines, double line_width, const char *line_color, double *x_poly, int *polys_groups, char **labels, float *fsz, float *r, float *g, float *b, const char *opacity, SparseMatrix A, FILE *f)
Definition make_map.c:326
void improve_contiguity(int n, int dim, int *grouping, SparseMatrix poly_point_map, double *x, SparseMatrix graph)
Definition make_map.c:115
static void conn_comp(int n, SparseMatrix A, int *groups, SparseMatrix *poly_point_map)
Definition make_map.c:464
#define cycle_prev(e)
Definition make_map.h:44
#define cycle_next(e)
Definition make_map.h:45
#define edge_tail(e)
Definition make_map.h:43
#define neighbor(t, i, edim, elist)
Definition make_map.h:41
#define edge_head(e)
Definition make_map.h:42
#define delta
Definition maze.c:133
static boxf bbox(Ppoly_t **obsp, int npoly, int *np)
static const int dim
int node_distinct_coloring(const char *color_scheme, int *lightness, bool weightedQ, SparseMatrix A0, double accuracy, int seed, int *cdim0, double **colors)
PATHUTIL_API COORD area2(Ppoint_t, Ppoint_t, Ppoint_t)
Definition visibility.c:44
static const int maxit
Definition power.c:16
#define PRISIZE_T
PRIu64 alike for printing size_t
Definition prisize_t.h:27
void stress_model(int dim, SparseMatrix B, double **x, int maxit_sm, int *flag)
Definition stress_model.c:9
graph or subgraph
Definition cgraph.h:425
double center[2]
Definition make_map.c:164
int vertices[3]
Definition make_map.c:163
Definition types.h:251
Definition geom.h:27
static point center(point vertex[], size_t n)
static mytime_t T
Definition timing.c:41
#define MAX(a, b)
Definition write.c:31