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