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