Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
minglemain.cpp
Go to the documentation of this file.
1
2/*************************************************************************
3 * Copyright (c) 2011 AT&T Intellectual Property
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-v10.html
8 *
9 *************************************************************************/
10
11#include "config.h"
12#include "../tools/openFile.h"
13#include <algorithm>
14#include <cgraph/cgraph.h>
15#include <cgraph/exit.h>
16#include <cgraph/ingraphs.h>
17#include <common/pointset.h>
18#include <getopt.h>
19#include <iomanip>
20#include <iostream>
21#include <sstream>
22#include <vector>
23
24#include <sparse/DotIO.h>
27
28typedef enum {
31} fmt_t;
32
33typedef struct {
35 int idx;
36} etoi_t;
37
38#define ED_idx(e) (((etoi_t*)AGDATA(e))->idx)
39
40typedef struct {
42 int method;
44 double K;
49 double angle;
50} opts_t;
51
52static char *fname;
53static FILE *outfile;
54
55static const char use_msg[] =
56"Usage: mingle <options> <file>\n\
57 -a t - max. turning angle [0-180] (40)\n\
58 -c i - compatability measure; 0 : distance, 1: full (default)\n\
59 -i iter: number of outer iterations/subdivisions (4)\n\
60 -k k - number of neighbors in the nearest neighbor graph of edges (10)\n\
61 -K k - the force constant\n\
62 -m method - method used. 0 (force directed), 1 (agglomerative ink saving, default), 2 (cluster+ink saving)\n\
63 -o fname - write output to file fname (stdout)\n\
64 -p t - balance for avoiding sharp angles\n\
65 The larger the t, the more sharp angles are allowed\n\
66 -r R - max. recursion level with agglomerative ink saving method (100)\n\
67 -T fmt - output format: gv (default) or simple\n\
68 -v - verbose\n";
69
70static void
72{
73 std::cerr << use_msg;
75}
76
77/* checkG:
78 * Return non-zero if g has loops or multiedges.
79 * Relies on multiedges occurring consecutively in edge list.
80 */
81static int
83{
84 Agedge_t* e;
85 Agnode_t* n;
86 Agnode_t* h;
87 Agnode_t* prevh = nullptr;
88
89 for (n = agfstnode (g); n; n = agnxtnode (g, n)) {
90 for (e = agfstout (g, n); e; e = agnxtout (g, e)) {
91 if ((h = aghead(e)) == n) return 1; // loop
92 if (h == prevh) return 1; // multiedge
93 prevh = h;
94 }
95 prevh = nullptr; // reset
96 }
97 return 0;
98}
99
100static void init(int argc, char *argv[], opts_t &opts) {
101 int c;
102 char* cmd = argv[0];
103 double s;
104 int i;
105
106 opterr = 0;
107 opts.outer_iter = 4;
110 opts.K = -1;
111 opts.fmt = FMT_GV;
112 opts.nneighbors = 10;
113 opts.max_recursion = 100;
114 opts.angle_param = -1;
115 opts.angle = 40.0/180.0*M_PI;
116
117 while ((c = getopt(argc, argv, ":a:c:i:k:K:m:o:p:r:T:v:?")) != -1) {
118 switch (c) {
119 case 'a':
120 if ((sscanf(optarg,"%lf",&s) > 0) && (s >= 0))
121 opts.angle = M_PI * s / 180;
122 else
123 std::cerr << "-a arg " << optarg << " must be positive real - ignored\n";
124 break;
125 case 'c':
126 if ((sscanf(optarg,"%d",&i) > 0) && (0 <= i) && (i <= COMPATIBILITY_FULL))
128 else
129 std::cerr << "-c arg " << optarg << " must be an integer in [0,"
130 << COMPATIBILITY_FULL << "] - ignored\n";
131 break;
132 case 'i':
133 if ((sscanf(optarg,"%d",&i) > 0) && (i >= 0))
134 opts.outer_iter = i;
135 else
136 std::cerr << "-i arg " << optarg << " must be a non-negative integer - "
137 "ignored\n";
138 break;
139 case 'k':
140 if ((sscanf(optarg,"%d",&i) > 0) && (i >= 2))
141 opts.nneighbors = i;
142 else
143 std::cerr << "-k arg " << optarg << " must be an integer >= 2 - ignored\n";
144 break;
145 case 'K':
146 if ((sscanf(optarg,"%lf",&s) > 0) && (s > 0))
147 opts.K = s;
148 else
149 std::cerr << "-K arg " << optarg << " must be positive real - ignored\n";
150 break;
151 case 'm':
152 if ((sscanf(optarg,"%d",&i) > 0) && (0 <= i) && (i <= METHOD_INK))
153 opts.method = i;
154 else
155 std::cerr << "-k arg " << optarg << " must be an integer >= 2 - ignored\n";
156 break;
157 case 'o':
158 outfile = openFile(cmd, optarg, "w");
159 break;
160 case 'p':
161 if ((sscanf(optarg,"%lf",&s) > 0))
163 else
164 std::cerr << "-p arg " << optarg << " must be real - ignored\n";
165 break;
166 case 'r':
167 if ((sscanf(optarg,"%d",&i) > 0) && (i >= 0))
169 else
170 std::cerr << "-r arg " << optarg << " must be a non-negative integer - "
171 "ignored\n";
172 break;
173 case 'T':
174 if (!strcmp(optarg, "gv"))
175 opts.fmt = FMT_GV;
176 else if (!strcmp(optarg,"simple"))
178 else
179 std::cerr << "-T arg " << optarg << " must be \"gv\" or \"simple\" - "
180 "ignored\n";
181 break;
182 case 'v':
183 Verbose = 1;
184 if ((sscanf(optarg,"%d",&i) > 0) && (i >= 0))
185 Verbose = static_cast<unsigned char>(i);
186 else
187 optind--;
188 break;
189 case ':':
190 if (optopt == 'v')
191 Verbose = 1;
192 else {
193 std::cerr << cmd << ": option -" << optopt << " missing argument\n";
194 usage(1);
195 }
196 break;
197 case '?':
198 if (optopt == '\0' || optopt == '?')
199 usage(0);
200 else {
201 std::cerr << cmd << ": option -" << optopt << " unrecognized\n";
202 usage(1);
203 }
204 break;
205 default:
206 break;
207 }
208 }
209 argv += optind;
210 argc -= optind;
211
212 if (argc > 0)
213 Files = argv;
214 if (!outfile) outfile = stdout;
215 if (Verbose) {
216 std::cerr
217 << "Mingle params:\n"
218 << " outer_iter = " << opts.outer_iter << '\n'
219 << " method = " << opts.method << '\n'
220 << " compatibility_method = " << opts.compatibility_method << '\n'
221 << " K = " << std::setprecision(2) << opts.K << '\n'
222 << " fmt = " << (opts.fmt ? "simple" : "gv") << '\n'
223 << " nneighbors = " << opts.nneighbors << '\n'
224 << " max_recursion = " << opts.max_recursion << '\n'
225 << " angle_param = " << std::setprecision(2) << opts.angle_param << '\n'
226 << " angle = " << std::setprecision(2) << (180 * opts.angle / M_PI) << '\n';
227 }
228}
229
230/* genBundleSpline:
231 * We have ninterval+1 points. We drop the ninterval-1 internal points, and add 4 points to the first
232 * and last intervals, and 3 to the rest, giving the needed 3*ninterval+4 points.
233 */
234static void genBundleSpline(const pedge &edge, std::ostream &os) {
235 int k, j;
236 const int dim = edge.dim;
237 const std::vector<double> &x = edge.x;
238
239 for (j = 0; j < edge.npoints; j++){
240 if (j != 0) {
241 os << ' ';
242 const std::vector<double> tt1{0.15, 0.5, 0.85};
243 const std::vector<double> tt2{0.15, 0.4, 0.6, 0.85};
244 const std::vector<double> &tt = (j == 1 || j == edge.npoints - 1) ? tt2 : tt1;
245 for (const double t : tt){
246 for (k = 0; k < dim; k++) {
247 if (k != 0) os << ',';
248 os << std::setprecision(3) << (x[(j-1)*dim+k]*(1-t)+x[j*dim+k]*t);
249 }
250 os << ' ';
251 }
252 }
253 if (j == 0 || j == edge.npoints - 1) {
254 for (k = 0; k < dim; k++) {
255 if (k != 0) os << ',';
256 os << std::setprecision(3) << x[j * dim + k];
257 }
258 }
259 }
260}
261
262static void genBundleInfo(const pedge &edge, std::ostream &os) {
263 int k, j;
264 const int dim = edge.dim;
265 const std::vector<double> &x = edge.x;
266
267 for (j = 0; j < edge.npoints; j++){
268 if (j != 0) os << ':';
269 for (k = 0; k < dim; k++) {
270 if (k != 0) os << ',';
271 os << std::setprecision(3) << x[j * dim + k];
272 }
273
274 if (j < edge.npoints - 1 && !edge.wgts.empty()) {
275 os << ';' << std::setprecision(3) << edge.wgts[j];
276 }
277 }
278}
279
280static void genBundleColors(const pedge &edge, std::ostream &os,
281 double maxwgt) {
282 int k, j, r, g, b;
283 double len, t, len_total0 = 0;
284 const int dim = edge.dim;
285 const std::vector<double> &x = edge.x;
286 std::vector<double> lens(edge.npoints);
287
288 for (j = 0; j < edge.npoints - 1; j++){
289 len = 0;
290 for (k = 0; k < dim; k++){
291 len += (x[dim*j+k] - x[dim*(j+1)+k])*(x[dim*j+k] - x[dim*(j+1)+k]);
292 }
293 lens[j] = len = sqrt(len/k);
294 len_total0 += len;
295 }
296 for (j = 0; j < edge.npoints - 1; j++){
297 t = edge.wgts[j] / maxwgt;
298 /* interpolate between red (t = 1) to blue (t = 0) */
299 r = 255*t; g = 0; b = 255*(1-t);
300 if (j != 0) os << ':';
301 os << std::hex << std::setw(2) << std::setfill('0') << '#' << r << g << b
302 << 85;
303 if (j < edge.npoints - 2) {
304 os << ';' << (lens[j] / len_total0);
305 }
306 }
307 os << std::dec << std::setw(0); // reset stream characteristics
308}
309
310static void export_dot(FILE *fp, int ne, const std::vector<pedge> &edges,
311 Agraph_t *g) {
312 Agsym_t* epos = agattr(g, AGEDGE, const_cast<char*>("pos"), "");
313 Agsym_t* esects = agattr(g, AGEDGE, const_cast<char*>("bundle"), "");
314 Agsym_t* eclrs = nullptr;
315 Agnode_t* n;
316 Agedge_t* e;
317 int i, j;
318 double maxwgt = 0;
319
320 /* figure out max number of bundled original edges in a pedge */
321 for (i = 0; i < ne; i++){
322 const pedge &edge = edges[i];
323 if (!edge.wgts.empty()) {
324 for (j = 0; j < edge.npoints - 1; j++){
325 maxwgt = std::max(maxwgt, edge.wgts[j]);
326 }
327 }
328 }
329
330 std::ostringstream buf;
331 for (n = agfstnode (g); n; n = agnxtnode (g, n)) {
332 for (e = agfstout (g, n); e; e = agnxtout (g, e)) {
333 const pedge &edge = edges[ED_idx(e)];
334
335 genBundleSpline(edge, buf);
336 agxset(e, epos, buf.str().c_str());
337 buf.str("");
338
339 genBundleInfo(edge, buf);
340 agxset(e, esects, buf.str().c_str());
341 buf.str("");
342
343 if (!edge.wgts.empty()) {
344 if (!eclrs) eclrs = agattr(g, AGEDGE, const_cast<char*>("color"), "");
345 genBundleColors(edge, buf, maxwgt);
346 agxset(e, eclrs, buf.str().c_str());
347 buf.str("");
348 }
349 }
350 }
351 agwrite(g, fp);
352}
353
354static int bundle(Agraph_t *g, const opts_t &opts) {
355 double *x = nullptr;
356 int dim = 2;
357 int i;
358 int rv = 0;
359
360 if (checkG(g)) {
361 agerrorf("Graph %s (%s) contains loops or multiedges\n", agnameof(g), fname);
362 return 1;
363 }
364 initDotIO(g);
366 if (!A){
367 agerrorf("Error: could not convert graph %s (%s) into matrix\n", agnameof(g), fname);
368 return 1;
369 }
370 if (x == nullptr) {
371 agerr (AGPREV, " in file %s\n", fname);
372 return 1;
373 }
374
375 A = SparseMatrix_symmetrize(A, true);
376 if (opts.fmt == FMT_GV) {
377 PointMap* pm = newPM(); /* map from node id pairs to edge index */
378 Agnode_t* n;
379 Agedge_t* e;
380 int idx = 0;
381
382 const int *ia = A->ia;
383 const int *ja = A->ja;
384 for (i = 0; i < A->m; i++){
385 for (int j = ia[i]; j < ia[i+1]; j++){
386 if (ja[j] > i){
387 insertPM (pm, i, ja[j], idx++);
388 }
389 }
390 }
391 for (i = 0, n = agfstnode(g); n; n = agnxtnode(g,n)) {
392 setDotNodeID(n, i++);
393 }
394 for (i = 0, n = agfstnode(g); n; n = agnxtnode(g,n)) {
395 for (e = agfstout (g, n); e; e = agnxtout (g, e)) {
396 i = getDotNodeID (agtail(e));
397 int j = getDotNodeID (aghead(e));
398 if (j < i) {
399 std::swap(i, j);
400 }
401 int k = insertPM (pm, i, j, -1);
402 assert (k >= 0);
403 agbindrec (e, "info", sizeof(etoi_t), true);
404 ED_idx(e) = k;
405 }
406 }
407 freePM (pm);
408 }
409
410 const int *ia = A->ia;
411 const int *ja = A->ja;
412 int nz = A->nz;
413 std::vector<double> xx(nz * 4);
414 nz = 0;
415 dim = 4;
416 for (i = 0; i < A->m; i++){
417 for (int j = ia[i]; j < ia[i+1]; j++){
418 if (ja[j] > i){
419 xx[nz*dim] = x[i*2];
420 xx[nz*dim+1] = x[i*2+1];
421 xx[nz*dim+2] = x[ja[j]*2];
422 xx[nz*dim+3] = x[ja[j]*2+1];
423 nz++;
424 }
425 }
426 }
427 if (Verbose)
428 std::cerr << "n = " << A->m << " nz = " << nz << '\n';
429
430 SparseMatrix B = nearest_neighbor_graph(nz, std::min(opts.nneighbors, nz), xx);
431
433 A = B;
434 free(x);
435
436 std::vector<pedge> edges =
440
441 if (opts.fmt == FMT_GV) {
442 export_dot(outfile, A->m, edges, g);
443 }
444 else {
445 pedge_export_gv(outfile, A->m, edges);
446 }
447 return rv;
448}
449
450int main(int argc, char *argv[])
451{
452 Agraph_t *g;
453 Agraph_t *prev = nullptr;
454 ingraph_state ig;
455 int rv = 0;
456 opts_t opts;
457
458 init(argc, argv, opts);
459 newIngraph(&ig, Files);
460
461 while ((g = nextGraph(&ig)) != 0) {
462 if (prev)
463 agclose(prev);
464 prev = g;
465 fname = fileName(&ig);
466 if (Verbose)
467 std::cerr << "Process graph " << agnameof(g) << " in file " << fname << '\n';
468 rv |= bundle(g, opts);
469 }
470
471 graphviz_exit(rv);
472}
473
SparseMatrix SparseMatrix_import_dot(Agraph_t *g, int dim, double **x, int format)
Definition DotIO.c:80
int getDotNodeID(Agnode_t *n)
Definition DotIO.c:664
void initDotIO(Agraph_t *g)
Definition DotIO.c:654
void setDotNodeID(Agnode_t *n, int v)
Definition DotIO.c:659
SparseMatrix SparseMatrix_symmetrize(SparseMatrix A, bool pattern_symmetric_only)
void SparseMatrix_delete(SparseMatrix A)
@ FORMAT_CSR
#define M_PI
Definition arith.h:41
char ** Files
Definition bcomps.c:61
abstract graph C library, Cgraph API
static char * cmd
Definition acyclic.c:40
std::vector< pedge > edge_bundling(SparseMatrix A0, int dim, const std::vector< double > &x, int maxit_outer, double K, int method, int nneighbor, int compatibility_method, int max_recursion, double angle_param, double angle)
void pedge_export_gv(FILE *fp, int ne, const std::vector< pedge > &edges)
@ METHOD_INK
@ METHOD_INK_AGGLOMERATE
@ COMPATIBILITY_FULL
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
#define A(n, t)
Definition expr.h:76
static int eval(Agraph_t *g, int root)
Definition gc.c:269
static double len(glCompPoint p)
Definition glutils.c:150
static int Verbose
Definition gml2gv.c:22
void free(void *)
edge
Definition gmlparse.y:279
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:341
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:481
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define agtail(e)
Definition cgraph.h:889
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
void agerrorf(const char *fmt,...)
Definition agerror.c:165
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:155
@ AGPREV
Definition cgraph.h:858
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:708
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
@ AGEDGE
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:88
static opts_t opts
Definition gvgen.c:394
static const char * usage
Definition gvpr.c:53
#define B
Definition hierarchy.c:117
$2 u p prev
Definition htmlparse.y:495
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Definition ingraphs.c:156
Agraph_t * nextGraph(ingraph_state *sp)
Definition ingraphs.c:61
ingraph_state * newIngraph(ingraph_state *sp, char **files)
Definition ingraphs.c:140
supports user-supplied data
#define ED_idx(e)
static const char use_msg[]
static int bundle(Agraph_t *g, const opts_t &opts)
static void genBundleSpline(const pedge &edge, std::ostream &os)
static void genBundleInfo(const pedge &edge, std::ostream &os)
static int checkG(Agraph_t *g)
fmt_t
@ FMT_SIMPLE
@ FMT_GV
static void init(int argc, char *argv[], opts_t &opts)
static void export_dot(FILE *fp, int ne, const std::vector< pedge > &edges, Agraph_t *g)
static FILE * outfile
static char * fname
static void genBundleColors(const pedge &edge, std::ostream &os, double maxwgt)
SparseMatrix nearest_neighbor_graph(int nPts, int num_neighbors, const std::vector< double > &x)
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
int insertPM(PointMap *pm, int x, int y, int value)
Definition pointset.c:149
PointMap * newPM(void)
Definition pointset.c:134
void freePM(PointMap *ps)
Definition pointset.c:144
point containers PointSet and PointMap
graph or subgraph
Definition cgraph.h:425
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
Definition cdt.h:104
Agrec_t hdr
int max_recursion
double angle_param
double K
int nneighbors
int method
fmt_t fmt
int compatibility_method
int outer_iter
double angle
int main()
Definition grammar.c:93