12#include "../tools/openFile.h"
20#include <unordered_map>
39#define ED_idx(e) (((etoi_t*)AGDATA(e))->idx)
58"Usage: mingle <options> <file>\n\
59 -a t - max. turning angle [0-180] (40)\n\
60 -c i - compatability measure; 0 : distance, 1: full (default)\n\
61 -i iter: number of outer iterations/subdivisions (4)\n\
62 -k k - number of neighbors in the nearest neighbor graph of edges (10)\n\
63 -K k - the force constant\n\
64 -m method - method used. 0 (force directed), 1 (agglomerative ink saving, default), 2 (cluster+ink saving)\n\
65 -o fname - write output to file fname (stdout)\n\
66 -p t - balance for avoiding sharp angles\n\
67 The larger the t, the more sharp angles are allowed\n\
68 -r R - max. recursion level with agglomerative ink saving method (100)\n\
69 -T fmt - output format: gv (default) or simple\n\
93 if ((h =
aghead(e)) == n)
return 1;
94 if (h == prevh)
return 1;
119 while ((c = getopt(argc, argv,
":a:c:i:k:K:m:o:p:r:T:v:?")) != -1) {
122 if (sscanf(optarg,
"%lf", &
s) > 0 &&
s >= 0)
125 std::cerr <<
"-a arg " << optarg <<
" must be positive real - ignored\n";
131 std::cerr <<
"-c arg " << optarg <<
" must be an integer in [0,"
135 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
138 std::cerr <<
"-i arg " << optarg <<
" must be a non-negative integer - "
142 if (sscanf(optarg,
"%d", &i) > 0 && i >= 2)
145 std::cerr <<
"-k arg " << optarg <<
" must be an integer >= 2 - ignored\n";
148 if (sscanf(optarg,
"%lf", &
s) > 0 &&
s > 0)
151 std::cerr <<
"-K arg " << optarg <<
" must be positive real - ignored\n";
154 if (sscanf(optarg,
"%d", &i) > 0 && 0 <= i && i <=
METHOD_INK)
157 std::cerr <<
"-k arg " << optarg <<
" must be an integer >= 2 - ignored\n";
163 if (sscanf(optarg,
"%lf", &
s) > 0)
166 std::cerr <<
"-p arg " << optarg <<
" must be real - ignored\n";
169 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
172 std::cerr <<
"-r arg " << optarg <<
" must be a non-negative integer - "
176 if (!strcmp(optarg,
"gv"))
178 else if (!strcmp(optarg,
"simple"))
181 std::cerr <<
"-T arg " << optarg <<
" must be \"gv\" or \"simple\" - "
186 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
187 Verbose =
static_cast<unsigned char>(i);
195 std::cerr <<
cmd <<
": option -" << optopt <<
" missing argument\n";
200 if (optopt ==
'\0' || optopt ==
'?')
203 std::cerr <<
cmd <<
": option -" << optopt <<
" unrecognized\n";
219 <<
"Mingle params:\n"
223 <<
" K = " << std::setprecision(2) <<
opts.
K <<
'\n'
224 <<
" fmt = " << (
opts.
fmt ?
"simple" :
"gv") <<
'\n'
227 <<
" angle_param = " << std::setprecision(2) <<
opts.
angle_param <<
'\n'
228 <<
" angle = " << std::setprecision(2) << (180 *
opts.
angle /
M_PI) <<
'\n';
239 const std::vector<double> &x =
edge.x;
241 for (j = 0; j <
edge.npoints; j++){
244 const std::vector<double> tt1{0.15, 0.5, 0.85};
245 const std::vector<double> tt2{0.15, 0.4, 0.6, 0.85};
246 const std::vector<double> &tt = (j == 1 || j ==
edge.npoints - 1) ? tt2 : tt1;
247 for (
const double t : tt){
248 for (k = 0; k <
dim; k++) {
249 if (k != 0) os <<
',';
250 os << std::setprecision(3) << (x[(j-1)*
dim+k]*(1-t)+x[j*
dim+k]*t);
255 if (j == 0 || j ==
edge.npoints - 1) {
256 for (k = 0; k <
dim; k++) {
257 if (k != 0) os <<
',';
258 os << std::setprecision(3) << x[j *
dim + k];
267 const std::vector<double> &x =
edge.x;
269 for (j = 0; j <
edge.npoints; j++){
270 if (j != 0) os <<
':';
271 for (k = 0; k <
dim; k++) {
272 if (k != 0) os <<
',';
273 os << std::setprecision(3) << x[j *
dim + k];
276 if (j <
edge.npoints - 1 && !
edge.wgts.empty()) {
277 os <<
';' << std::setprecision(3) <<
edge.wgts[j];
285 double len, t, len_total0 = 0;
287 const std::vector<double> &x =
edge.x;
288 std::vector<double> lens(
edge.npoints);
290 for (j = 0; j <
edge.npoints - 1; j++){
292 for (k = 0; k <
dim; k++){
295 lens[j] =
len = sqrt(
len/k);
298 for (j = 0; j <
edge.npoints - 1; j++){
299 t =
edge.wgts[j] / maxwgt;
301 r = 255*t; g = 0; b = 255*(1-t);
302 if (j != 0) os <<
':';
303 os << std::hex << std::setw(2) << std::setfill(
'0') <<
'#' << r << g << b
305 if (j <
edge.npoints - 2) {
306 os <<
';' << (lens[j] / len_total0);
309 os << std::dec << std::setw(0);
312static void export_dot(FILE *fp,
int ne,
const std::vector<pedge> &edges,
323 for (i = 0; i < ne; i++){
325 if (!
edge.wgts.empty()) {
326 for (j = 0; j <
edge.npoints - 1; j++){
327 maxwgt = std::max(maxwgt,
edge.wgts[j]);
332 std::ostringstream buf;
338 agxset(e, epos, buf.str().c_str());
342 agxset(e, esects, buf.str().c_str());
345 if (!
edge.wgts.empty()) {
346 if (!eclrs) eclrs =
agattr(g,
AGEDGE,
const_cast<char*
>(
"color"),
"");
348 agxset(e, eclrs, buf.str().c_str());
360 return std::hash<int>{}(x.first) ^ std::hash<int>{}(x.second);
393 const int *ia =
A->ia;
394 const int *ja =
A->ja;
395 for (i = 0; i <
A->m; i++){
396 for (
int j = ia[i]; j < ia[i+1]; j++){
398 pm[std::pair(i, ja[j])] = idx++;
412 const int k = pm[std::pair(i, j)];
419 const int *ia =
A->ia;
420 const int *ja =
A->ja;
422 std::vector<double> xx(nz * 4);
425 for (i = 0; i <
A->m; i++){
426 for (
int j = ia[i]; j < ia[i+1]; j++){
429 xx[nz*
dim+1] = x[i*2+1];
430 xx[nz*
dim+2] = x[ja[j]*2];
431 xx[nz*
dim+3] = x[ja[j]*2+1];
437 std::cerr <<
"n = " <<
A->m <<
" nz = " << nz <<
'\n';
445 std::vector<pedge> edges =
459int main(
int argc,
char *argv[])
476 std::cerr <<
"Process graph " <<
agnameof(g) <<
" in file " <<
fname <<
'\n';
SparseMatrix SparseMatrix_import_dot(Agraph_t *g, int dim, double **x, int format)
int getDotNodeID(Agnode_t *n)
void initDotIO(Agraph_t *g)
void setDotNodeID(Agnode_t *n, int v)
SparseMatrix SparseMatrix_symmetrize(SparseMatrix A, bool pattern_symmetric_only)
void SparseMatrix_delete(SparseMatrix A)
abstract graph C library, Cgraph API
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)
static NORETURN void graphviz_exit(int status)
static int eval(Agraph_t *g, int root)
static double len(glCompPoint p)
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
int agxset(void *obj, Agsym_t *sym, const char *value)
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
void agerrorf(const char *fmt,...)
int agerr(agerrlevel_t level, const char *fmt,...)
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Agnode_t * agfstnode(Agraph_t *g)
char * agnameof(void *)
returns a string descriptor for the object.
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
static const char * usage
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Agraph_t * nextGraph(ingraph_state *sp)
ingraph_state * newIngraph(ingraph_state *sp, char **files)
supports user-supplied data
static const char use_msg[]
std::unordered_map< std::pair< int, int >, int, PointHash > PointMap
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)
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 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)
implementation of Agrec_t
a hash derivation function for int pairs
size_t operator()(const std::pair< int, int > &x) const