Graphviz 14.1.3~dev.20260207.0611
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 {
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 min_grp, max_grp;
440
441 min_grp = max_grp = groups[0];
442 for (i = 0; i < n; i++) {
443 max_grp = MAX(groups[i], max_grp);
444 min_grp = MIN(groups[i], min_grp);
445 }
446 if (min_grp <= 0) return NULL;
447 B = SparseMatrix_new(max_grp, max_grp, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
448 ia = A->ia;
449 ja = A->ja;
450 for (i = 0; i < n; i++){
451 ig1 = groups[i]-1;/* add a diagonal entry */
453 for (j = ia[i]; j < ia[i+1]; j++){
454 jj = ja[j];
455 if (i != jj && groups[i] != groups[jj] && groups[jj] != GRP_RANDOM && groups[jj] != GRP_BBOX){
456 ig1 = groups[i]-1; ig2 = groups[jj]-1;
458 }
459 }
460 }
463 return BB;
464}
465
466static void conn_comp(int n, SparseMatrix A, int *groups, SparseMatrix *poly_point_map){
467 /* form a graph where only vertices that are connected as well as in the same group are connected */
468 int *ia, *ja;
469 int one = 1, jj, i, j;
470 SparseMatrix B, BB;
471 int ncomps, *comps = NULL;
472
474 ia = A->ia;
475 ja = A->ja;
476 for (i = 0; i < n; i++){
477 for (j = ia[i]; j < ia[i+1]; j++){
478 jj = ja[j];
479 if (i != jj && groups[i] == groups[jj]){
481 }
482 }
483 }
485
486 int *comps_ptr = SparseMatrix_weakly_connected_components(BB, &ncomps, &comps);
489 *poly_point_map = SparseMatrix_new(ncomps, n, (size_t)n, MATRIX_TYPE_PATTERN,
490 FORMAT_CSR);
491 free((*poly_point_map)->ia);
492 free((*poly_point_map)->ja);
493 (*poly_point_map)->ia = comps_ptr;
494 (*poly_point_map)->ja = comps;
495 (*poly_point_map)->nz = (size_t)n;
496
497}
498
499static void get_poly_lines(int nt, SparseMatrix E, int ncomps, int *comps_ptr,
500 int *comps, int *groups, SparseMatrix *poly_lines,
501 int **polys_groups, int GRP_RANDOM, int GRP_BBOX) {
502 /*============================================================
503
504 polygon outlines
505
506 ============================================================*/
507 int i, *tlist, nz, ipoly, nnt, ii, jj, t1, t2, t, cur, next, nn, j, nlink, sta;
508 int *elist, edim = 3;/* a list tell which vertex a particular vertex is linked with during poly construction.
509 since the surface is a cycle, each can only link with 2 others, the 3rd position is used to record how many links
510 */
511 int *ie = E->ia, *je = E->ja, *e = E->a;
513
514 int *mask = gv_calloc(nt, sizeof(int));
515 for (i = 0; i < nt; i++) mask[i] = -1;
516 /* loop over every point in each connected component */
517 elist = gv_calloc(nt * edim, sizeof(int));
518 tlist = gv_calloc(nt * 2, sizeof(int));
519 *poly_lines = SparseMatrix_new(ncomps, nt, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
520 *polys_groups = gv_calloc(ncomps, sizeof(int));
521
522 for (i = 0; i < nt; i++) elist[i*edim + 2] = 0;
523 nz = ie[E->m] - ie[0];
524
525 ipoly = 1;
526
527 for (i = 0; i < ncomps; i++){
528 nnt = 0;
529 for (j = comps_ptr[i]; j < comps_ptr[i+1]; j++){
530 ii = comps[j];
531
532 (*polys_groups)[i] = groups[ii];/* assign the grouping of each poly */
533
534 /* skip the country formed by random points */
535 if (groups[ii] == GRP_RANDOM || groups[ii] == GRP_BBOX) continue;
536
537 for (jj = ie[ii]; jj < ie[ii+1]; jj++){
538 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 */
539 t1 = e[jj];
540 t2 = e[jj+1];
541
542 nlink = elist[t1*edim + 2]%2;
543 elist[t1*edim + nlink] = t2;/* t1->t2*/
544 elist[t1*edim + 2]++;
545
546 nlink = elist[t2*edim + 2]%2;
547 elist[t2*edim + nlink] = t1;/* t1->t2*/
548 elist[t2*edim + 2]++;
549
550 tlist[nnt++] = t1; tlist[nnt++] = t2;
551 jj++;
552 }
553 }
554 }/* done poly edges for this component i */
555
556 /* form one or more (if there is a hole) polygon outlines for this component */
557 for (j = 0; j < nnt; j++){
558 t = tlist[j];
559 if (mask[t] != i){
560 cur = sta = t; mask[cur] = i;
561 next = neighbor(t, 1, edim, elist);
562 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, cur, &ipoly);
563 while (next != sta){
564 mask[next] = i;
565
566 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, next, &ipoly);
567
568 nn = neighbor(next, 0, edim, elist);
569 if (nn == cur) {
570 nn = neighbor(next, 1, edim, elist);
571 }
572 assert(nn != cur);
573
574 cur = next;
575 next = nn;
576 }
577
578 SparseMatrix_coordinate_form_add_entry(*poly_lines, i, sta, &ipoly);/* complete a cycle by adding starting point */
579
580 ipoly++;
581 }
582
583 }/* found poly_lines for this comp */
584 }
585
587 SparseMatrix_delete(*poly_lines);
588 *poly_lines = A;
589
590 free(tlist);
591 free(elist);
592 free(mask);
593}
594
595static void cycle_print(int head, int *cycle, int *edge_table){
596 int cur, next;
597
598 cur = head;
599 fprintf(stderr, "cycle (edges): {");
600 while ((next = cycle_next(cur)) != head){
601 fprintf(stderr, "%d,",cur);
602 cur = next;
603 }
604 fprintf(stderr, "%d}\n",cur);
605
606 cur = head;
607 fprintf(stderr, "cycle (vertices): ");
608 while ((next = cycle_next(cur)) != head){
609 fprintf(stderr, "%d--",edge_head(cur));
610 cur = next;
611 }
612 fprintf(stderr, "%d--%d\n",edge_head(cur),edge_tail(cur));
613}
614
615static int same_edge(int ecur, int elast, int *edge_table){
616 return (edge_head(ecur) == edge_head(elast) && edge_tail(ecur) == edge_tail(elast))
617 || (edge_head(ecur) == edge_tail(elast) && edge_tail(ecur) == edge_head(elast));
618}
619
620static void get_polygon_solids(int nt, SparseMatrix E, int ncomps,
621 int *comps_ptr, int *comps, SparseMatrix *polys)
622{
623 /*============================================================
624
625 polygon solids that will be colored
626
627 ============================================================*/
628 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
629 t1 and t2, then from u there are two edges to v, one denoted as t1->t2, and the other t2->t1. They are
630 numbered as e1 and e2. edge_table[e1]={t1,t2} and edge_table[e2]={t2,t1}
631 */
632 SparseMatrix half_edges;/* a graph of triangle edges. If two vertex u and v are connected and are adjacent to two triangles
633 t1 and t2, then from u there are two edges to v, one denoted as t1->t2, and the other t2->t1. They are
634 numbered as e1 and e2. Likewise from v to u there are also two edges e1 and e2.
635 */
636
637 int n = E->m, *ie = E->ia, *je = E->ja, *e = E->a, ne, i, j, t1, t2, jj, ii;
638 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,
639 cycle[e][1] gives the next edge
640 */
641 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) */
642 int *emask;/* whether an edge is seen this iter */
643 enum {NO_DUPLICATE = -1};
644 int *elist, edim = 3;/* a list tell which edge a particular vertex is linked with when a voro cell has been visited,
645 since the surface is a cycle, each vertex can only link with 2 edges, the 3rd position is used to record how many links
646 */
647
648 int k, duplicate, ee = 0, ecur, enext, eprev, cur, next, nn, nlink, head, elast = 0, etail, tail, ehead, efirst;
649
650 int DEBUG_CYCLE = 0;
652
653 edge_table = gv_calloc(E->nz * 2, sizeof(int));
654
655 half_edges = SparseMatrix_new(n, n, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
656
657 ne = 0;
658 for (i = 0; i < n; i++){
659 for (j = ie[i]; j < ie[i+1]; j++){
660 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*/
661 t1 = e[j];
662 t2 = e[j+1];
663 jj = je[j];
664 assert(jj < n);
665 edge_table[ne*2] = t1;/*t1->t2*/
666 edge_table[ne*2+1] = t2;
667 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, i, jj, &ne);
668 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, jj, i, &ne);
669 ne++;
670
671 edge_table[ne*2] = t2;/*t2->t1*/
672 edge_table[ne*2+1] = t1;
673 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, i, jj, &ne);
674 half_edges = SparseMatrix_coordinate_form_add_entry(half_edges, jj, i, &ne);
675
676
677 ne++;
678 j++;
679 }
680 }
681 }
682 assert(E->nz >= (size_t)ne);
683
684 cycle = gv_calloc(ne * 2, sizeof(int));
686 SparseMatrix_delete(half_edges);half_edges = B;
687
688 edge_cycle_map = gv_calloc(ne, sizeof(int));
689 emask = gv_calloc(ne, sizeof(int));
690 for (i = 0; i < ne; i++) edge_cycle_map[i] = NOT_ON_CYCLE;
691 for (i = 0; i < ne; i++) emask[i] = -1;
692
693 ie = half_edges->ia;
694 je = half_edges->ja;
695 e = half_edges->a;
696 elist = gv_calloc(nt * 3, sizeof(int));
697 for (i = 0; i < nt; i++) elist[i*edim + 2] = 0;
698
699 *polys = SparseMatrix_new(ncomps, nt, 1, MATRIX_TYPE_INTEGER, FORMAT_COORD);
700
701 for (i = 0; i < ncomps; i++){
702 if (DEBUG_CYCLE) fprintf(stderr, "\n ============ comp %d has %d members\n",i, comps_ptr[i+1]-comps_ptr[i]);
703 for (k = comps_ptr[i]; k < comps_ptr[i+1]; k++){
704 ii = comps[k];
705 duplicate = NO_DUPLICATE;
706 if (DEBUG_CYCLE) fprintf(stderr,"member = %d has %d neighbors\n",ii, ie[ii+1]-ie[ii]);
707 for (j = ie[ii]; j < ie[ii+1]; j++){
708 jj = je[j];
709 ee = e[j];
710 t1 = edge_head(ee);
711 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));
712 nlink = elist[t1*edim + 2]%2;
713 elist[t1*edim + nlink] = ee;/* t1->t2*/
714 elist[t1*edim + 2]++;
715
716 if (edge_cycle_map[ee] != NOT_ON_CYCLE) duplicate = ee;
717 emask[ee] = ii;
718 }
719
720 if (duplicate == NO_DUPLICATE){
721 /* this must be the first time the cycle is being established, a new voro cell*/
722 ecur = ee;
723 cycle_head = ecur;
724 cycle_next(ecur) = ecur;
725 cycle_prev(ecur) = ecur;
726 edge_cycle_map[ecur] = 1;
727 head = cur = edge_head(ecur);
728 next = edge_tail(ecur);
729 if (DEBUG_CYCLE) fprintf(stderr, "NEW CYCLE\n starting with edge %d, {head,tail}={%d,%d}\n", ee, head, next);
730 while (next != head){
731 enext = neighbor(next, 0, edim, elist);/* two voro edges linked with triangle "next" */
732 if ((edge_head(enext) == cur && edge_tail(enext) == next)
733 || (edge_head(enext) == next && edge_tail(enext) == cur)){/* same edge */
734 enext = neighbor(next, 1, edim, elist);
735 };
736 if (DEBUG_CYCLE) fprintf(stderr, "cur edge = %d, next edge %d, {head,tail}={%d,%d},\n",ecur, enext, edge_head(enext), edge_tail(enext));
737 nn = edge_head(enext);
738 if (nn == next) nn = edge_tail(enext);
739 cycle_next(enext) = cycle_next(ecur);
740 cycle_prev(enext) = ecur;
741 cycle_next(ecur) = enext;
742 cycle_prev(ee) = enext;
743 edge_cycle_map[enext] = 1;
744
745 ecur = enext;
746 cur = next;
747 next = nn;
748 }
749 if (DEBUG_CYCLE) cycle_print(ee, cycle,edge_table);
750 } else {
751 /* we found a duplicate edge, remove that, and all contiguous neighbors that overlap with the current voro
752 */
753 ecur = ee = duplicate;
754 while (emask[ecur] == ii){
755 /* contiguous overlapping edges, Cycling is not possible
756 since the cycle can not complete surround the new voro cell and yet
757 do not contain any other edges
758 */
759 ecur = cycle_next(ecur);
760 }
761 if (DEBUG_CYCLE) fprintf(stderr," duplicating edge = %d, starting from the a non-duplicating edge %d, search backwards\n",ee, ecur);
762
763 ecur = cycle_prev(ecur);
764 efirst = ecur;
765 while (emask[ecur] == ii){
766 if (DEBUG_CYCLE) fprintf(stderr," remove edge %d (%d--%d)\n",ecur, edge_head(ecur), edge_tail(ecur));
767 /* short this duplicating edge */
768 edge_cycle_map[ecur] = NOT_ON_CYCLE;
769 enext = cycle_next(ecur);
770 eprev = cycle_prev(ecur);
771 cycle_next(ecur) = ecur;/* isolate this edge */
772 cycle_prev(ecur) = ecur;
773 cycle_next(eprev) = enext;/* short */
774 cycle_prev(enext) = eprev;
775 elast = ecur;/* record the last removed edge */
776 ecur = eprev;
777 }
778
779 if (DEBUG_CYCLE) {
780 fprintf(stderr, "remaining (broken) cycle = ");
781 cycle_print(cycle_next(ecur), cycle,edge_table);
782 }
783
784 /* we now have a broken cycle of head = edge_tail(ecur) and tail = edge_head(cycle_next(ecur)) */
785 ehead = ecur; etail = cycle_next(ecur);
786 cycle_head = ehead;
787 head = edge_tail(ehead);
788 tail = edge_head(etail);
789
790 /* pick an edge ev from head in the voro that is a removed edge: since the removed edges form a path starting from
791 efirst, and at elast (head of elast is head), usually we just need to check that ev is not the same as elast,
792 but in the case of a voro filling in a hole, we also need to check that ev is not efirst,
793 since in this case every edge of the voro cell is removed
794 */
795 ecur = neighbor(head, 0, edim, elist);
796 if (same_edge(ecur, elast, edge_table)){
797 ecur = neighbor(head, 1, edim, elist);
798 };
799
800 if (DEBUG_CYCLE) fprintf(stderr, "forwarding now from edge %d = {%d, %d}, try to reach vtx %d, first edge from voro = %d\n",
801 ehead, edge_head(ehead), edge_tail(ehead), tail, ecur);
802
803 /* now go along voro edges till we reach the tail of the broken cycle*/
804 cycle_next(ehead) = ecur;
805 cycle_prev(ecur) = ehead;
806 cycle_prev(etail) = ecur;
807 cycle_next(ecur) = etail;
808 if (same_edge(ecur, efirst, edge_table)){
809 if (DEBUG_CYCLE) fprintf(stderr, "this voro cell fill in a hole completely!!!!\n");
810 } else {
811
812 edge_cycle_map[ecur] = 1;
813 head = cur = edge_head(ecur);
814 next = edge_tail(ecur);
815 if (DEBUG_CYCLE) fprintf(stderr, "starting with edge %d, {head,tail}={%d,%d}\n", ecur, head, next);
816 while (next != tail){
817 enext = neighbor(next, 0, edim, elist);/* two voro edges linked with triangle "next" */
818 if ((edge_head(enext) == cur && edge_tail(enext) == next)
819 || (edge_head(enext) == next && edge_tail(enext) == cur)){/* same edge */
820 enext = neighbor(next, 1, edim, elist);
821 };
822 if (DEBUG_CYCLE) fprintf(stderr, "cur edge = %d, next edge %d, {head,tail}={%d,%d},\n",ecur, enext, edge_head(enext), edge_tail(enext));
823
824
825 nn = edge_head(enext);
826 if (nn == next) nn = edge_tail(enext);
827 cycle_next(enext) = cycle_next(ecur);
828 cycle_prev(enext) = ecur;
829 cycle_next(ecur) = enext;
830 cycle_prev(etail) = enext;
831 edge_cycle_map[enext] = 1;
832
833 ecur = enext;
834 cur = next;
835 next = nn;
836 }
837 }
838
839 }
840
841 }
842 /* done this component, load to sparse matrix, unset edge_map*/
843 ecur = cycle_head;
844 while ((enext = cycle_next(ecur)) != cycle_head){
845 edge_cycle_map[ecur] = NOT_ON_CYCLE;
846 head = edge_head(ecur);
848 ecur = enext;
849 }
850 edge_cycle_map[ecur] = NOT_ON_CYCLE;
851 head = edge_head(ecur); tail = edge_tail(ecur);
853 SparseMatrix_coordinate_form_add_entry(*polys, i, tail, &i);
854
855
856 /* unset edge_map */
857 }
858
860 SparseMatrix_delete(*polys);
861 *polys = B;
862
863 SparseMatrix_delete(half_edges);
864 free(cycle);
865 free(edge_cycle_map);
866 free(elist);
867 free(emask);
868 free(edge_table);
869}
870
871static void get_polygons(int n, int nrandom, int dim, int *grouping, int nt,
872 struct Triangle *Tp, SparseMatrix E, int *nverts,
873 double **x_poly, SparseMatrix *poly_lines,
874 SparseMatrix *polys, int **polys_groups,
875 SparseMatrix *poly_point_map,
876 SparseMatrix *country_graph) {
877 int i, j;
878 int *groups;
879 int maxgrp;
880 int *comps = NULL, *comps_ptr = NULL, ncomps;
881 int GRP_RANDOM, GRP_BBOX;
882
883 assert(dim == 2);
884 *nverts = nt;
885
886 groups = gv_calloc(n + nrandom, sizeof(int));
887 maxgrp = grouping[0];
888 for (i = 0; i < n; i++) {
889 maxgrp = MAX(maxgrp, grouping[i]);
890 groups[i] = grouping[i];
891 }
892
893 GRP_RANDOM = maxgrp + 1; GRP_BBOX = maxgrp + 2;
894 for (i = n; i < n + nrandom - 4; i++) {/* all random points in the same group */
895 groups[i] = GRP_RANDOM;
896 }
897 for (i = n + nrandom - 4; i < n + nrandom; i++) {/* last 4 pts of the expanded bonding box in the same group */
898 groups[i] = GRP_BBOX;
899 }
900
901 /* finding connected components: vertices that are connected in the triangle graph, as well as in the same group */
902 conn_comp(n + nrandom, E, groups, poly_point_map);
903
904 ncomps = (*poly_point_map)->m;
905 comps = (*poly_point_map)->ja;
906 comps_ptr = (*poly_point_map)->ia;
907
908 /* connected components are such that the random points and the bounding box 4 points forms the last
909 remaining components */
910 for (i = ncomps - 1; i >= 0; i--) {
911 if (groups[comps[comps_ptr[i]]] != GRP_RANDOM &&
912 groups[comps[comps_ptr[i]]] != GRP_BBOX) break;
913 }
914 ncomps = i + 1;
915 GV_INFO("ncomps = %d", ncomps);
916
917 *x_poly = gv_calloc(dim * nt, sizeof(double));
918 for (i = 0; i < nt; i++){
919 for (j = 0; j < dim; j++){
920 (*x_poly)[i*dim+j] = Tp[i].center[j];
921 }
922 }
923
924 /*============================================================
925
926 polygon outlines
927
928 ============================================================*/
929 get_poly_lines(nt, E, ncomps, comps_ptr, comps, groups, poly_lines,
930 polys_groups, GRP_RANDOM, GRP_BBOX);
931
932 /*============================================================
933
934 polygon solids
935
936 ============================================================*/
937 get_polygon_solids(nt, E, ncomps, comps_ptr, comps, polys);
938
939 *country_graph = get_country_graph(n, E, groups, GRP_RANDOM, GRP_BBOX);
940
941 free(groups);
942}
943
944static int make_map_internal(bool include_OK_points, int n, int dim, double *x0,
945 int *grouping0, SparseMatrix graph,
946 double bounding_box_margin, int nrandom,
947 int nedgep, double shore_depth_tol, int *nverts,
948 double **x_poly, SparseMatrix *poly_lines,
949 SparseMatrix *polys, int **polys_groups,
950 SparseMatrix *poly_point_map,
951 SparseMatrix *country_graph, int highlight_cluster) {
952
953
954 double xmax[2], xmin[2], area, *x = x0;
955 int i, j;
956 QuadTree qt = NULL;
957 int dim2 = 2, nn = 0;
958 int max_qtree_level = 10;
959 double ymin[2], min;
960 int imin, nzok = 0, nzok0 = 0, nt;
961 double *xran, point[2];
962 struct Triangle *Tp;
964 double boxsize[2];
965 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,
966 including them instead of throwing away increase realism of boundary */
967 int *grouping = grouping0;
968
969 int HIGHLIGHT_SET = highlight_cluster;
970
971 for (j = 0; j < dim2; j++) {
972 xmax[j] = x[j];
973 xmin[j] = x[j];
974 }
975
976 for (i = 0; i < n; i++){
977 for (j = 0; j < dim2; j++) {
978 xmax[j] = fmax(xmax[j], x[i*dim+j]);
979 xmin[j] = fmin(xmin[j], x[i*dim+j]);
980 }
981 }
982 boxsize[0] = xmax[0] - xmin[0];
983 boxsize[1] = xmax[1] - xmin[1];
984 area = boxsize[0]*boxsize[1];
985
986 if (nrandom == 0) {
987 nrandom = n;
988 } else if (nrandom < 0){
989 nrandom = -nrandom * n;
990 } else if (nrandom < 4) {/* by default we add 4 point on 4 corners anyway */
991 nrandom = 0;
992 } else {
993 nrandom -= 4;
994 }
995
996 if (shore_depth_tol < 0) shore_depth_tol = sqrt(area/(double) n); /* set to average distance for random distribution */
997 GV_INFO("nrandom=%d shore_depth_tol=%.08f", nrandom, shore_depth_tol);
998
999
1000 /* add artificial points along each edge to avoid as much as possible
1001 two connected components be separated due to small shore depth */
1002 {
1003 int nz;
1004 double *y;
1005 int k, t, np=nedgep;
1006 if (graph && np){
1007 fprintf(stderr,"add art np = %d\n",np);
1008 assert(graph->nz <= INT_MAX);
1009 nz = (int)graph->nz;
1010 y = gv_calloc(dim * n + dim * nz * np, sizeof(double));
1011 for (i = 0; i < n*dim; i++) y[i] = x[i];
1012 grouping = gv_calloc(n + nz * np, sizeof(int));
1013 for (i = 0; i < n; i++) grouping[i] = grouping0[i];
1014 nz = n;
1015 for (i = 0; i < graph->m; i++){
1016
1017 for (j = graph->ia[i]; j < graph->ia[i+1]; j++){
1018 if (!HIGHLIGHT_SET || (grouping[i] == grouping[graph->ja[j]] && grouping[i] == HIGHLIGHT_SET)){
1019 for (t = 0; t < np; t++){
1020 for (k = 0; k < dim; k++){
1021 y[nz*dim+k] = t/((double) np)*x[i*dim+k] + (1-t/((double) np))*x[(graph->ja[j])*dim + k];
1022 }
1023 assert(n + (nz-n)*np + t < n + nz*np && n + (nz-n)*np + t >= 0);
1024 if (t/((double) np) > 0.5){
1025 grouping[nz] = grouping[i];
1026 } else {
1027 grouping[nz] = grouping[graph->ja[j]];
1028 }
1029 nz++;
1030 }
1031 }
1032 }
1033 }
1034 fprintf(stderr, "after adding edge points, n:%d->%d\n",n, nz);
1035 n = nz;
1036 x = y;
1037 qt = QuadTree_new_from_point_list(dim, nz, max_qtree_level, y);
1038 } else {
1039 qt = QuadTree_new_from_point_list(dim, n, max_qtree_level, x);
1040 }
1041 }
1042
1043 /* generate random points for lake/sea effect */
1044 if (nrandom != 0){
1045 for (i = 0; i < dim2; i++) {
1046 if (bounding_box_margin > 0){
1047 xmin[i] -= bounding_box_margin;
1048 xmax[i] += bounding_box_margin;
1049 } else if (bounding_box_margin < 0) {
1050 xmin[i] -= boxsize[i]*(-bounding_box_margin);
1051 xmax[i] += boxsize[i]*(-bounding_box_margin);
1052 } else { // auto bounding box
1053 xmin[i] -= fmax(boxsize[i] * 0.2, 2.* shore_depth_tol);
1054 xmax[i] += fmax(boxsize[i] * 0.2, 2 * shore_depth_tol);
1055 }
1056 }
1057 if (Verbose) {
1058 double bbm = bounding_box_margin;
1059 if (bbm > 0)
1060 fprintf (stderr, "bounding box margin: %.06f", bbm);
1061 else if (bbm < 0)
1062 fprintf (stderr, "bounding box margin: (%.06f * %.06f)", boxsize[0], -bbm);
1063 else
1064 fprintf(stderr, "bounding box margin: %.06f",
1065 fmax(boxsize[0] * 0.2, 2 * shore_depth_tol));
1066 }
1067 if (nrandom < 0) {
1068 const double area2 = (xmax[1] - xmin[1]) * (xmax[0] - xmin[0]);
1069 const double n1 = floor(area2 / (shore_depth_tol * shore_depth_tol));
1070 const double n2 = n * floor(area2 / area);
1071 nrandom = fmax(n1, n2);
1072 }
1073 srand(123);
1074 xran = gv_calloc((nrandom + 4) * dim2, sizeof(double));
1075 int nz = 0;
1076 if (INCLUDE_OK_POINTS){
1077 nzok0 = nzok = nrandom - 1;/* points that are within tolerance of real or artificial points */
1078 if (grouping == grouping0) {
1079 int *grouping2 = gv_calloc(n + nrandom, sizeof(int));
1080 memcpy(grouping2, grouping, sizeof(int)*n);
1081 grouping = grouping2;
1082 } else {
1083 grouping = gv_recalloc(grouping, n, n + nrandom, sizeof(int));
1084 }
1085 }
1086 nn = n;
1087
1088 for (i = 0; i < nrandom; i++){
1089
1090 for (j = 0; j < dim2; j++){
1091 point[j] = xmin[j] + (xmax[j] - xmin[j])*drand();
1092 }
1093
1094 QuadTree_get_nearest(qt, point, ymin, &imin, &min);
1095
1096 if (min > shore_depth_tol){/* point not too close, accepted */
1097 for (j = 0; j < dim2; j++){
1098 xran[nz*dim2+j] = point[j];
1099 }
1100 nz++;
1101 } else if (INCLUDE_OK_POINTS && min > shore_depth_tol/10){/* avoid duplicate points */
1102 for (j = 0; j < dim2; j++){
1103 xran[nzok*dim2+j] = point[j];
1104 }
1105 grouping[nn++] = grouping[imin];
1106 nzok--;
1107
1108 }
1109
1110 }
1111 nrandom = nz;
1112 if (Verbose) fprintf(stderr, "nn nrandom=%d\n", nrandom);
1113 } else {
1114 xran = gv_calloc(4 * dim2, sizeof(double));
1115 }
1116
1117
1118
1119 /* add 4 corners even if nrandom = 0. The corners should be further away from the other points to avoid skinny triangles */
1120 for (i = 0; i < dim2; i++) xmin[i] -= 0.2*(xmax[i]-xmin[i]);
1121 for (i = 0; i < dim2; i++) xmax[i] += 0.2*(xmax[i]-xmin[i]);
1122 i = nrandom;
1123 for (j = 0; j < dim2; j++) xran[i*dim2+j] = xmin[j];
1124 i++;
1125 for (j = 0; j < dim2; j++) xran[i*dim2+j] = xmax[j];
1126 i++;
1127 xran[i*dim2] = xmin[0]; xran[i*dim2+1] = xmax[1];
1128 i++;
1129 xran[i*dim2] = xmax[0]; xran[i*dim2+1] = xmin[1];
1130 nrandom += 4;
1131
1132
1133 double *xcombined;
1134 if (INCLUDE_OK_POINTS){
1135 xcombined = gv_calloc((nn + nrandom) * dim2, sizeof(double));
1136 } else {
1137 xcombined = gv_calloc((n + nrandom) * dim2, sizeof(double));
1138 }
1139 for (i = 0; i < n; i++) {
1140 for (j = 0; j < dim2; j++) xcombined[i*dim2+j] = x[i*dim+j];
1141 }
1142 for (i = 0; i < nrandom; i++) {
1143 for (j = 0; j < dim2; j++) xcombined[(i + nn)*dim2+j] = xran[i*dim+j];
1144 }
1145
1146 if (INCLUDE_OK_POINTS){
1147 for (i = 0; i < nn - n; i++) {
1148 for (j = 0; j < dim2; j++) xcombined[(i + n)*dim2+j] = xran[(nzok0 - i)*dim+j];
1149 }
1150 n = nn;
1151 }
1152
1153
1154 {
1155 int nz, nh = 0;/* the set to highlight */
1156 if (HIGHLIGHT_SET){
1157 if (Verbose) fprintf(stderr," highlight cluster %d, n = %d\n",HIGHLIGHT_SET, n);
1158 /* shift set to the beginning */
1159 nz = 0;
1160 for (i = 0; i < n; i++){
1161 if (grouping[i] == HIGHLIGHT_SET){
1162 nh++;
1163 for (j = 0; j < dim; j++){
1164 xcombined[nz++] = x[i*dim+j];
1165 }
1166 }
1167 }
1168 for (i = 0; i < n; i++){
1169 if (grouping[i] != HIGHLIGHT_SET){
1170 for (j = 0; j < dim; j++){
1171 xcombined[nz++] = x[i*dim+j];
1172 }
1173 }
1174 }
1175 assert(nz == n*dim);
1176 for (i = 0; i < nh; i++){
1177 grouping[i] = 1;
1178 }
1179 for (i = nh; i < n; i++){
1180 grouping[i] = 2;
1181 }
1182 nrandom += n - nh;/* count everything except cluster HIGHLIGHT_SET as random */
1183 n = nh;
1184 if (Verbose) fprintf(stderr,"nh = %d\n",nh);
1185 }
1186 }
1187
1188 int rc = 0;
1189 if (get_tri(n + nrandom, dim2, xcombined, &nt, &Tp, &E) != 0) {
1190 rc = -1;
1191 goto done;
1192 }
1193 get_polygons(n, nrandom, dim2, grouping, nt, Tp, E, nverts, x_poly,
1194 poly_lines, polys, polys_groups, poly_point_map, country_graph);
1195
1197 free(Tp);
1198done:
1199 free(xcombined);
1200 free(xran);
1201 if (grouping != grouping0) free(grouping);
1202 QuadTree_delete(qt);
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 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)
@ MATRIX_TYPE_PATTERN
@ MATRIX_TYPE_INTEGER
@ FORMAT_COORD
@ FORMAT_CSR
#define SparseMatrix_coordinate_form_add_entry(A, irn, jcn, val)
wrap SparseMatrix_coordinate_form_add_entry_ for type safety
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:944
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:871
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:433
static int same_edge(int ecur, int elast, int *edge_table)
Definition make_map.c:615
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:499
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:595
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:620
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:466
#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