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