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