Graphviz 14.1.2~dev.20260121.0931
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#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 {double *dist = NULL;
58 A = SparseMatrix_symmetrize(A0, false);
62 free(dist);
64 SparseMatrix_export(stdout, A);
65 }
66
67 // lightness: of the form 0,70, specifying the range of lightness of LAB
68 // color. Ignored if scheme is not COLOR_LAB.
69 int lightness[] = {0, 100};
70
71 // accuracy is the threshold given so that when finding the coloring for each
72 // node, the optimal is with in "accuracy" of the true global optimal.
73 const double accuracy = 0.01;
74
75 // seed: random_seed. If negative, consider -seed as the number of random
76 // start iterations
77 const int seed = -10;
78
79 node_distinct_coloring(color_scheme, lightness, weightedQ, A, accuracy, seed,
80 &cdim, &colors);
81
82 if (A != A0){
84 }
85 *rgb_r = gv_calloc(n + 1, sizeof(float));
86 *rgb_g = gv_calloc(n + 1, sizeof(float));
87 *rgb_b = gv_calloc(n + 1, sizeof(float));
88
89 for (i = 0; i < n; i++){
90 (*rgb_r)[i+1] = (float) colors[cdim*i];
91 (*rgb_g)[i+1] = (float) colors[cdim*i + 1];
92 (*rgb_b)[i+1] = (float) colors[cdim*i + 2];
93 }
94 free(colors);
95}
96
97void map_optimal_coloring(int seed, SparseMatrix A, float *rgb_r, float *rgb_g, float *rgb_b){
98 int *p = NULL;
99 float *u = NULL;
100 int n = A->m;
101 int i;
102
104
105 rgb_r++; rgb_b++; rgb_g++;/* seems necessary, but need to better think about cases when clusters are not contiguous */
106 vector_float_take(n, rgb_r, n, p, &u);
107 for (i = 0; i < n; i++) rgb_r[i] = u[i];
108 vector_float_take(n, rgb_g, n, p, &u);
109 for (i = 0; i < n; i++) rgb_g[i] = u[i];
110 vector_float_take(n, rgb_b, n, p, &u);
111 for (i = 0; i < n; i++) rgb_b[i] = u[i];
112 free(u);
113}
114
115static int get_poly_id(int ip, SparseMatrix point_poly_map){
116 return point_poly_map->ja[point_poly_map->ia[ip]];
117}
118
119void improve_contiguity(int n, int dim, int *grouping, SparseMatrix poly_point_map, double *x, SparseMatrix graph){
120 /*
121 grouping: which group each of the vertex belongs to
122 poly_point_map: a matrix of dimension npolys x (n + nrandom), poly_point_map[i,j] != 0 if polygon i contains the point j.
123 . If j < n, it is the original point, otherwise it is artificial point (forming the rectangle around a label) or random points.
124 */
125 int i, j, *ia, *ja, u, v;
126 SparseMatrix point_poly_map, D;
127 double dist;
128 int nbad = 0;
129 int maxit = 10;
130
132
133 assert(graph->m == n);
134 ia = D->ia; ja = D->ja;
135 double *a = D->a;
136
137 /* point_poly_map: each row i has only 1 entry at column j, which says that point i is in polygon j */
138 point_poly_map = SparseMatrix_transpose(poly_point_map);
139
140 for (i = 0; i < n; i++){
141 u = i;
142 for (j = ia[i]; j < ia[i+1]; j++){
143 v = ja[j];
144 dist = distance_cropped(x, dim, u, v);
145 if (grouping[u] != grouping[v]){
146 a[j] = 1.1*dist;
147 } else if (get_poly_id(u, point_poly_map) == get_poly_id(v, point_poly_map)){
148 a[j] = dist;
149 } else {
150 nbad++;
151 a[j] = 0.9*dist;
152 }
153
154 }
155 }
156
157 GV_INFO("ratio (edges among discontiguous regions vs total edges)=%f", (double)nbad / ia[n]);
158 const int flag = stress_model(dim, D, x, maxit);
159
160 assert(!flag);
161
163 SparseMatrix_delete(point_poly_map);
164}
165
166struct Triangle {
167 int vertices[3];/* 3 points */
168 double center[2]; /* center of the triangle */
169};
170
171static void normal(double v[], double normal[]){
172 if (v[0] == 0){
173 normal[0] = 1; normal[1] = 0;
174 } else {
175 normal[0] = -v[1];
176 normal[1] = v[0];
177 }
178}
179
180static void triangle_center(double x[], double y[], double z[], double c[]){
181 /* find the "center" c, which is the intersection of the 3 vectors that are normal to each
182 of the edges respectively, and which passes through the center of the edges respectively
183 center[{x_, y_, z_}] := Module[
184 {xy = 0.5*(x + y), yz = 0.5*(y + z), zx = 0.5*(z + x), nxy, nyz,
185 beta, cen},
186 nxy = normal[y - x];
187 nyz = normal[y - z];
188 beta = (y-x).(xy - yz)/(nyz.(y-x));
189 cen = yz + beta*nyz;
190 Graphics[{Line[{x, y, z, x}], Red, Point[cen], Line[{cen, xy}],
191 Line[{cen, yz}], Green, Line[{cen, zx}]}]
192
193 ]
194 */
195 double xy[2], yz[2], nxy[2], nyz[2], ymx[2], ymz[2], beta, bot;
196 int i;
197
198 for (i = 0; i < 2; i++) ymx[i] = y[i] - x[i];
199 for (i = 0; i < 2; i++) ymz[i] = y[i] - z[i];
200 for (i = 0; i < 2; i++) xy[i] = 0.5*(x[i] + y[i]);
201 for (i = 0; i < 2; i++) yz[i] = 0.5*(y[i] + z[i]);
202
203
204 normal(ymx, nxy);
205 normal(ymz, nyz);
206 bot = nyz[0]*(x[0]-y[0])+nyz[1]*(x[1]-y[1]);
207 if (bot == 0){/* xy and yz are parallel */
208 c[0] = xy[0]; c[1] = xy[1];
209 return;
210 }
211 beta = ((x[0] - y[0])*(xy[0] - yz[0])+(x[1] - y[1])*(xy[1] - yz[1]))/bot;
212 c[0] = yz[0] + beta*nyz[0];
213 c[1] = yz[1] + beta*nyz[1];
214}
215
216static SparseMatrix matrix_add_entry(SparseMatrix A, int i, int j, int val){
217 int i1 = i, j1 = j;
218 if (i < j) {
219 i1 = j; j1 = i;
220 }
222 return SparseMatrix_coordinate_form_add_entry(A, i1, j1, &val);
223}
224
225static void plot_dot_edges(FILE *f, SparseMatrix A){
226 int i, *ia, *ja, j;
227
228
229 int n = A->m;
230 ia = A->ia;
231 ja = A->ja;
232 for (i = 0; i < n; i++){
233 for (j = ia[i]; j < ia[i+1]; j++){
234 if (ja[j] == i) continue;
235 fprintf(f,"%d -- %d;\n",i,ja[j]);
236 }
237 }
238}
239
240static void plot_dot_labels(FILE *f, int n, int dim, double *x, char **labels, float *fsz){
241 int i;
242
243 for (i = 0; i < n; i++){
244 if (fsz){
245 fprintf(f, "%d [label=\"%s\", pos=\"%lf,%lf\", fontsize=%f];\n",i, labels[i], x[i*dim], x[i*dim+1], fsz[i]);
246 } else {
247 fprintf(f, "%d [label=\"%s\", pos=\"%lf,%lf\"];\n",i, labels[i], x[i*dim], x[i*dim+1]);
248 }
249 }
250
251}
252
253typedef LIST(double) doubles_t;
254
255static void dot_polygon(agxbuf *sbuff, doubles_t xp, doubles_t yp,
256 double line_width, bool fill, const char *cstring) {
257
258 assert(LIST_SIZE(&xp) == LIST_SIZE(&yp));
259 if (!LIST_IS_EMPTY(&xp)){
260 if (fill) {
261 agxbprint(sbuff,
262 " c %" PRISIZE_T " -%s C %" PRISIZE_T " -%s P %" PRISIZE_T " ",
263 strlen(cstring), cstring, strlen(cstring), cstring,
264 LIST_SIZE(&xp));
265 } else {
266 if (line_width > 0){
267 size_t len_swidth = (size_t)snprintf(NULL, 0, "%f", line_width);
268 agxbprint(sbuff, " c %" PRISIZE_T " -%s S %" PRISIZE_T
269 " -setlinewidth(%f) L %" PRISIZE_T " ", strlen(cstring), cstring,
270 len_swidth + 14, line_width, LIST_SIZE(&xp));
271 } else {
272 agxbprint(sbuff, " c %" PRISIZE_T " -%s L %" PRISIZE_T " ", strlen(cstring),
273 cstring, LIST_SIZE(&xp));
274 }
275 }
276 for (size_t i = 0; i < LIST_SIZE(&xp); i++) {
277 agxbprint(sbuff, " %f %f", LIST_GET(&xp, i), LIST_GET(&yp, i));
278 }
279 }
280}
281
282static void plot_dot_polygons(agxbuf *sbuff, double line_width,
283 const char *line_color, SparseMatrix polys,
284 double *x_poly, int *polys_groups, float *r,
285 float *g, float *b, const char *opacity) {
286 int i, j, *ia = polys->ia, *ja = polys->ja, *a = polys->a, npolys = polys->m, nverts = polys->n, ipoly,first;
287 const bool fill = false;
288 const bool use_line = line_width >= 0;
289
290 agxbuf cstring_buffer = {0};
291 const char *cstring = "#aaaaaaff";
292
293 doubles_t xp = {0};
294 doubles_t yp = {0};
295
296 GV_INFO("npolys = %d", npolys);
297 first = abs(a[0]); ipoly = first + 1;
298 for (i = 0; i < npolys; i++){
299 for (j = ia[i]; j < ia[i+1]; j++){
300 assert(ja[j] < nverts && ja[j] >= 0);
301 (void)nverts;
302 if (abs(a[j]) != ipoly){/* the first poly, or a hole */
303 ipoly = abs(a[j]);
304 if (r && g && b) {
305 rgb2hex(r[polys_groups[i]], g[polys_groups[i]], b[polys_groups[i]],
306 &cstring_buffer, opacity);
307 cstring = agxbuse(&cstring_buffer);
308 }
309 dot_polygon(sbuff, xp, yp, line_width, fill, cstring);
310 // start a new polygon
311 LIST_CLEAR(&xp);
312 LIST_CLEAR(&yp);
313 }
314 LIST_APPEND(&xp, x_poly[2 * ja[j]]);
315 LIST_APPEND(&yp, x_poly[2 * ja[j] + 1]);
316 }
317 if (use_line) {
318 dot_polygon(sbuff, xp, yp, line_width, fill, line_color);
319 } else {
320 /* why set fill to polys_groups[i]?*/
321 dot_polygon(sbuff, xp, yp, -1, true, cstring);
322 }
323 }
324 agxbfree(&cstring_buffer);
325 LIST_FREE(&xp);
326 LIST_FREE(&yp);
327}
328
329void plot_dot_map(Agraph_t* gr, int n, int dim, double *x, SparseMatrix polys,
330 SparseMatrix poly_lines, double line_width,
331 const char *line_color, double *x_poly, int *polys_groups,
332 char **labels, float *fsz, float *r, float *g, float *b,
333 const char* opacity, SparseMatrix A, FILE* f) {
334 /* if graph object exist, we just modify some attributes, otherwise we dump the whole graph */
335 bool plot_polyQ = true;
336 agxbuf sbuff = {0};
337
338 if (!r || !g || !b) plot_polyQ = false;
339
340 if (!gr) {
341 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");
342 } else {
343 agattr_text(gr, AGNODE, "margin", "0");
344 agattr_text(gr, AGNODE, "width", "0.0001");
345 agattr_text(gr, AGNODE, "height", "0.0001");
346 agattr_text(gr, AGNODE, "shape", "plaintext");
347 agattr_text(gr, AGNODE, "margin", "0");
348 agattr_text(gr, AGNODE, "fontname", "Helvetica-Bold");
349 agattr_text(gr, AGRAPH, "outputorder", "edgesfirst");
350 agattr_text(gr, AGRAPH, "bgcolor", "#dae2ff");
351 if (!A) agattr_text(gr, AGEDGE, "style","invis");/* do not plot edges */
352 }
353
354 /*polygons */
355 if (plot_polyQ) {
356 if (!gr) fprintf(f,"_background = \"");
357 plot_dot_polygons(&sbuff, -1., NULL, polys, x_poly, polys_groups, r, g, b, opacity);
358 }
359
360 /* polylines: line width is set here */
361 if (line_width >= 0){
362 plot_dot_polygons(&sbuff, line_width, line_color, poly_lines, x_poly, polys_groups, NULL, NULL, NULL, NULL);
363 }
364 if (!gr) {
365 fprintf(f,"%s",agxbuse(&sbuff));
366 fprintf(f,"\"\n");/* close polygons/lines */
367 } else {
368 agattr_text(gr, AGRAPH, "_background", agxbuse(&sbuff));
369 agwrite(gr, f);
370 }
371
372 /* nodes */
373 if (!gr && labels) plot_dot_labels(f, n, dim, x, labels, fsz);
374 /* edges */
375 if (!gr && A) plot_dot_edges(f, A);
376
377 /* background color + plot label?*/
378
379 if (!gr) fprintf(f, "}\n");
380
381 agxbfree(&sbuff);
382}
383
398static int get_tri(int n, int dim, double *x, int *nt, struct Triangle **T,
399 SparseMatrix *E) {
400 int i, j, i0, i1, i2, ntri;
402
403 int* trilist = get_triangles(x, n, &ntri);
404 if (trilist == NULL) {
405 return -1;
406 }
407
408 *T = gv_calloc(ntri, sizeof(struct Triangle));
409
411 for (i = 0; i < ntri; i++) {
412 for (j = 0; j < 3; j++) {
413 (*T)[i].vertices[j] = trilist[i * 3 + j];
414 }
415 i0 = (*T)[i].vertices[0]; i1 = (*T)[i].vertices[1]; i2 = (*T)[i].vertices[2];
416
417 triangle_center(&x[i0*dim], &x[i1*dim], &x[i2*dim], (*T)[i].center);
418 A = matrix_add_entry(A, i0, i1, i);
419 A = matrix_add_entry(A, i1, i2, i);
420 A = matrix_add_entry(A, i2, i0, i);
421 }
422
426 *E = B;
427
428 *nt = ntri;
429
430 free(trilist);
431 return 0;
432}
433
434static SparseMatrix get_country_graph(int n, SparseMatrix A, int *groups, int GRP_RANDOM, int GRP_BBOX){
435 /* form a graph each vertex is a group (a country), and a vertex is connected to another if the two countries shares borders.
436 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! */
437 int *ia, *ja;
438 int one = 1, jj, i, j, ig1, ig2;
439 SparseMatrix B, BB;
440 int min_grp, max_grp;
441
442 min_grp = max_grp = groups[0];
443 for (i = 0; i < n; i++) {
444 max_grp = MAX(groups[i], max_grp);
445 min_grp = MIN(groups[i], min_grp);
446 }
447 if (min_grp <= 0) return NULL;
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;
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 if (x != x0) free(x);
1204 return rc;
1205}
1206
1207static void add_point(int *n, int igrp, double **x, int *nmax, double point[], int **groups){
1208
1209 if (*n >= *nmax){
1210 int old_nmax = *nmax;
1211 *nmax = 20 + *n;
1212 *x = gv_recalloc(*x, 2 * old_nmax, 2 * *nmax, sizeof(double));
1213 *groups = gv_recalloc(*groups, old_nmax, *nmax, sizeof(int));
1214 }
1215
1216 (*x)[(*n)*2] = point[0];
1217 (*x)[(*n)*2+1] = point[1];
1218 (*groups)[*n] = igrp;
1219 (*n)++;
1220}
1221
1222static void get_boundingbox(int n, int dim, double *x, double *width, double *bbox){
1223 int i;
1224 bbox[0] = bbox[1] = x[0];
1225 bbox[2] = bbox[3] = x[1];
1226
1227 for (i = 0; i < n; i++){
1228 bbox[0] = fmin(bbox[0], x[i * dim] - width[i * dim]);
1229 bbox[1] = fmax(bbox[1], x[i * dim] + width[i * dim]);
1230 bbox[2] = fmin(bbox[2], x[i * dim + 1] - width[i * dim + 1]);
1231 bbox[3] = fmax(bbox[3], x[i * dim + 1] + width[i * dim + 1]);
1232 }
1233}
1234
1235int make_map_from_rectangle_groups(bool include_OK_points,
1236 int n, int dim, double *x, double *sizes,
1237 int *grouping, SparseMatrix graph, double bounding_box_margin, int nrandom, int *nart, int nedgep,
1238 double shore_depth_tol,
1239 int *nverts, double **x_poly,
1240 SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map,
1241 SparseMatrix *country_graph, int highlight_cluster){
1242
1243 /* 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
1244 geometrically will be in the same polygon describing the outline of the group. The main difference for this function and
1245 make_map_from_point_groups is that in this function, the input are points with width/heights, and we try not to place
1246 "lakes" inside these rectangles. This is achieved approximately by adding artificial points along the perimeter of the rectangles,
1247 as well as near the center.
1248
1249 input:
1250 include_OK_points: OK points are random points inserted and found to be within shore_depth_tol of real/artificial points,
1251 . including them instead of throwing away increase realism of boundary
1252 n: number of points
1253 dim: dimension of the points. If dim > 2, only the first 2D is used.
1254 x: coordinates
1255 sizes: width and height
1256 grouping: which group each of the vertex belongs to
1257 graph: the link structure between points. If graph == NULL, this is not used. otherwise
1258 . it is assumed that matrix is symmetric and the graph is undirected
1259 bounding_box_margin: margin used to form the bounding box.
1260 . if negative, it is taken as relative. i.e., -0.5 means a margin of 0.5*box_size
1261 nrandom (input): number of random points to insert in the bounding box to figure out lakes and seas.
1262 . If nrandom = 0, no points are inserted, if nrandom < 0, the number is decided automatically.
1263 .
1264 nart: on entry, number of artificial points to be added along each side of a rectangle enclosing the labels. if < 0, auto-selected.
1265 . On exit, actual number of artificial points added.
1266 nedgep: number of artificial points are adding along edges to establish as much as possible a bright between nodes
1267 . connected by the edge, and avoid islands that are connected. k = 0 mean no points.
1268 shore_depth_tol: nrandom random points are inserted in the bounding box of the points,
1269 . such random points are then weeded out if it is within distance of shore_depth_tol from
1270 . real points. If 0, auto assigned
1271
1272 output:
1273 nverts: number of vertices in the Voronoi diagram
1274 x_poly: the 2D coordinates of these polygons, dimension nverts*2
1275 poly_lines: the sparse matrix representation of the polygon indices, as well as their identity. The matrix is of size
1276 . npolygons x nverts. The i-th polygon is formed by linking vertices with index in the i-th row of the sparse matrix.
1277 . 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,
1278 . and l1 -- l2 -- ... form another. Each row can have more than 1 loop only when the connected region the polylines represent
1279 . has at least 1 holes.
1280 polys: the sparse matrix representation of the polygon indices, as well as their identity. The matrix is of size
1281 . npolygons x nverts. The i-th polygon is formed by linking vertices with index in the i-th row of the sparse matrix.
1282 . Unlike poly_lines, here each row represent an one stroke drawing of the SOLID polygon, vertices
1283 . along this path may repeat
1284 polys_groups: the group (color) each polygon belongs to, this include all groups of the real points,
1285 . plus the random point group and the bounding box group
1286 poly_point_map: a matrix of dimension npolys x (n + nrandom), poly_point_map[i,j] != 0 if polygon i contains the point j.
1287 . If j < n, it is the original point, otherwise it is artificial point (forming the rectangle around a label) or random points.
1288 country_graph: shows which country is a neighbor of which country.
1289 . if country i and country j are neighbor, then the {i,j} entry is the total number of vertices that
1290 . belongs to i and j, and share an edge of the triangulation. In addition, {i,i} and {j,j} have values equal
1291 . to the number of vertices in each of the countries. If the input "grouping" has negative or zero value, then
1292 . country_graph = NULL.
1293
1294
1295 */
1296 double *X;
1297 int N, nmax, i, j, igrp;
1298 int *groups;
1299 double K = *nart; // average number of points added per side of rectangle
1300
1301 double avgsize[2], avgsz, h[2], p1, p0;
1302 double point[2];
1303 double bbox[4];
1304
1305 if (K < 0){
1306 K = round(10 / (1 + n / 400.0)); // 0 if n > 3600
1307 }
1308 *nart = 0;
1309 if (Verbose){
1310 int maxgp = grouping[0];
1311 int mingp = grouping[0];
1312 for (i = 0; i < n; i++) {
1313 maxgp = MAX(maxgp, grouping[i]);
1314 mingp = MIN(mingp, grouping[i]);
1315 }
1316 fprintf(stderr, "max grouping - min grouping + 1 = %d\n",maxgp - mingp + 1);
1317 }
1318
1319 int rc = 0;
1320 if (!sizes){
1321 return make_map_internal(include_OK_points, n, dim, x, grouping, graph,
1322 bounding_box_margin, nrandom, nedgep,
1323 shore_depth_tol, nverts, x_poly, poly_lines, polys,
1324 polys_groups, poly_point_map, country_graph,
1325 highlight_cluster);
1326 } else {
1327
1328 /* add artificial node due to node sizes */
1329 avgsize[0] = 0;
1330 avgsize[1] = 0;
1331 for (i = 0; i < n; i++){
1332 for (j = 0; j < 2; j++) {
1333 avgsize[j] += sizes[i*dim+j];
1334 }
1335 }
1336 for (i = 0; i < 2; i++) avgsize[i] /= n;
1337 avgsz = 0.5*(avgsize[0] + avgsize[1]);
1338 GV_INFO("avgsize = {%f, %f}", avgsize[0], avgsize[1]);
1339
1340 nmax = 2*n;
1341 X = gv_calloc(dim * (n + nmax), sizeof(double));
1342 groups = gv_calloc(n + nmax, sizeof(int));
1343 for (i = 0; i < n; i++) {
1344 groups[i] = grouping[i];
1345 for (j = 0; j < 2; j++){
1346 X[i*2+j] = x[i*dim+j];
1347 }
1348 }
1349 N = n;
1350
1351 if (shore_depth_tol < 0) {
1352 shore_depth_tol = -(shore_depth_tol)*avgsz;
1353 } else if (shore_depth_tol == 0){
1354 get_boundingbox(n, dim, x, sizes, bbox);
1355 const double area = (bbox[1] - bbox[0]) * (bbox[3] - bbox[2]);
1356 shore_depth_tol = sqrt(area / n);
1357 GV_INFO("setting shore length ======%f", shore_depth_tol);
1358 }
1359
1360 /* add artificial points in an anti-clockwise fashion */
1361
1362 double delta[2] = {0};
1363 if (K > 0){
1364 delta[0] = .5*avgsize[0]/K; delta[1] = .5*avgsize[1]/K;/* small perturbation to make boundary between labels looks more fractal */
1365 }
1366 for (i = 0; i < n; i++){
1367 igrp = grouping[i];
1368 double nadded[2] = {0};
1369 for (j = 0; j < 2; j++) {
1370 if (avgsz > 0){
1371 nadded[j] = round(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 (double 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 (double 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 (double 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 (double 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:684
QuadTree QuadTree_new_from_point_list(int dim, int n, int max_level, double *coord)
Definition QuadTree.c:311
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_from_dense(int m, int n, double *x)
SparseMatrix SparseMatrix_new(int m, int n, size_t nz, int type, int format)
@ MATRIX_TYPE_PATTERN
@ MATRIX_TYPE_INTEGER
@ FORMAT_COORD
@ FORMAT_CSR
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:1008
#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:336
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(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_CLEAR(list)
Definition list.h:240
#define LIST_APPEND(list, item)
Definition list.h:120
#define LIST_FREE(list)
Definition list.h:370
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_GET(list, index)
Definition list.h:155
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:180
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:225
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:1207
static SparseMatrix get_country_graph(int n, SparseMatrix A, int *groups, int GRP_RANDOM, int GRP_BBOX)
Definition make_map.c:434
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:1222
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:1235
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:398
static SparseMatrix matrix_add_entry(SparseMatrix A, int i, int j, int val)
Definition make_map.c:216
static void normal(double v[], double normal[])
Definition make_map.c:171
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:97
static void plot_dot_labels(FILE *f, int n, int dim, double *x, char **labels, float *fsz)
Definition make_map.c:240
static int get_poly_id(int ip, SparseMatrix point_poly_map)
Definition make_map.c:115
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:282
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:329
void improve_contiguity(int n, int dim, int *grouping, SparseMatrix poly_point_map, double *x, SparseMatrix graph)
Definition make_map.c:119
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:168
int vertices[3]
Definition make_map.c:167
Definition types.h:251
Definition geom.h:27
static point center(point vertex[], size_t n)
static clock_t T
Definition timing.c:19