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