12#include "../tools/openFile.h"
20#include <unordered_map>
40#define ED_idx(e) (((etoi_t*)AGDATA(e))->idx)
59"Usage: mingle <options> <file>\n\
60 -a t - max. turning angle [0-180] (40)\n\
61 -c i - compatibility measure; 0 : distance, 1: full (default)\n\
62 -i iter: number of outer iterations/subdivisions (4)\n\
63 -k k - number of neighbors in the nearest neighbor graph of edges (10)\n\
64 -K k - the force constant\n\
65 -m method - method used. 0 (force directed), 1 (agglomerative ink saving, default), 2 (cluster+ink saving)\n\
66 -o fname - write output to file fname (stdout)\n\
67 -p t - balance for avoiding sharp angles\n\
68 The larger the t, the more sharp angles are allowed\n\
69 -r R - max. recursion level with agglomerative ink saving method (100)\n\
70 -T fmt - output format: gv (default) or simple\n\
94 if ((h =
aghead(e)) == n)
return 1;
95 if (h == prevh)
return 1;
120 while ((c = getopt(argc, argv,
":a:c:i:k:K:m:o:p:r:T:v:?")) != -1) {
123 if (sscanf(optarg,
"%lf", &
s) > 0 &&
s >= 0)
126 std::cerr <<
"-a arg " << optarg <<
" must be positive real - ignored\n";
132 std::cerr <<
"-c arg " << optarg <<
" must be an integer in [0,"
136 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
139 std::cerr <<
"-i arg " << optarg <<
" must be a non-negative integer - "
143 if (sscanf(optarg,
"%d", &i) > 0 && i >= 2)
146 std::cerr <<
"-k arg " << optarg <<
" must be an integer >= 2 - ignored\n";
149 if (sscanf(optarg,
"%lf", &
s) > 0 &&
s > 0)
152 std::cerr <<
"-K arg " << optarg <<
" must be positive real - ignored\n";
155 if (sscanf(optarg,
"%d", &i) > 0 && 0 <= i && i <=
METHOD_INK)
158 std::cerr <<
"-k arg " << optarg <<
" must be an integer >= 2 - ignored\n";
164 if (sscanf(optarg,
"%lf", &
s) > 0)
167 std::cerr <<
"-p arg " << optarg <<
" must be real - ignored\n";
170 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
173 std::cerr <<
"-r arg " << optarg <<
" must be a non-negative integer - "
177 if (!strcmp(optarg,
"gv"))
179 else if (!strcmp(optarg,
"simple"))
182 std::cerr <<
"-T arg " << optarg <<
" must be \"gv\" or \"simple\" - "
187 if (sscanf(optarg,
"%d", &i) > 0 && i >= 0)
188 Verbose =
static_cast<unsigned char>(i);
196 std::cerr <<
cmd <<
": option -" << optopt <<
" missing argument\n";
201 if (optopt ==
'\0' || optopt ==
'?')
204 std::cerr <<
cmd <<
": option -" << optopt <<
" unrecognized\n";
220 <<
"Mingle params:\n"
224 <<
" K = " << std::setprecision(2) <<
opts.
K <<
'\n'
225 <<
" fmt = " << (
opts.
fmt ?
"simple" :
"gv") <<
'\n'
228 <<
" angle_param = " << std::setprecision(2) <<
opts.
angle_param <<
'\n'
229 <<
" angle = " << std::setprecision(2) << (180 *
opts.
angle /
M_PI) <<
'\n';
240 const std::vector<double> &x =
edge.x;
242 for (j = 0; j <
edge.npoints; j++){
245 const std::vector<double> tt1{0.15, 0.5, 0.85};
246 const std::vector<double> tt2{0.15, 0.4, 0.6, 0.85};
247 const std::vector<double> &tt = (j == 1 || j ==
edge.npoints - 1) ? tt2 : tt1;
248 for (
const double t : tt){
249 for (k = 0; k <
dim; k++) {
250 if (k != 0) os <<
',';
251 os << std::setprecision(3) << (x[(j-1)*
dim+k]*(1-t)+x[j*
dim+k]*t);
256 if (j == 0 || j ==
edge.npoints - 1) {
257 for (k = 0; k <
dim; k++) {
258 if (k != 0) os <<
',';
259 os << std::setprecision(3) << x[j *
dim + k];
268 const std::vector<double> &x =
edge.x;
270 for (j = 0; j <
edge.npoints; j++){
271 if (j != 0) os <<
':';
272 for (k = 0; k <
dim; k++) {
273 if (k != 0) os <<
',';
274 os << std::setprecision(3) << x[j *
dim + k];
277 if (j <
edge.npoints - 1 && !
edge.wgts.empty()) {
278 os <<
';' << std::setprecision(3) <<
edge.wgts[j];
286 double len, t, len_total0 = 0;
288 const std::vector<double> &x =
edge.x;
289 std::vector<double> lens(
edge.npoints);
291 for (j = 0; j <
edge.npoints - 1; j++){
293 for (k = 0; k <
dim; k++){
296 lens[j] =
len = sqrt(
len/k);
299 for (j = 0; j <
edge.npoints - 1; j++){
300 t =
edge.wgts[j] / maxwgt;
302 r = 255*t; g = 0; b = 255*(1-t);
303 if (j != 0) os <<
':';
304 os << std::hex << std::setw(2) << std::setfill(
'0') <<
'#' << r << g << b
306 if (j <
edge.npoints - 2) {
307 os <<
';' << (lens[j] / len_total0);
310 os << std::dec << std::setw(0);
313static void export_dot(FILE *fp,
int ne,
const std::vector<pedge> &edges,
324 for (i = 0; i < ne; i++){
326 if (!
edge.wgts.empty()) {
327 for (j = 0; j <
edge.npoints - 1; j++){
328 maxwgt = std::max(maxwgt,
edge.wgts[j]);
333 std::ostringstream buf;
339 agxset(e, epos, buf.str().c_str());
343 agxset(e, esects, buf.str().c_str());
346 if (!
edge.wgts.empty()) {
349 agxset(e, eclrs, buf.str().c_str());
361 return std::hash<int>{}(x.first) ^ std::hash<int>{}(x.second);
394 const int *ia =
A->ia;
395 const int *ja =
A->ja;
396 for (i = 0; i <
A->m; i++){
397 for (
int j = ia[i]; j < ia[i+1]; j++){
399 pm[std::pair(i, ja[j])] = idx++;
413 const int k = pm[std::pair(i, j)];
420 const int *ia =
A->ia;
421 const int *ja =
A->ja;
423 std::vector<double> xx(nz * 4);
426 for (i = 0; i <
A->m; i++){
427 for (
int j = ia[i]; j < ia[i+1]; j++){
430 xx[nz*
dim+1] = x[i*2+1];
431 xx[nz*
dim+2] = x[ja[j]*2];
432 xx[nz*
dim+3] = x[ja[j]*2+1];
445 std::vector<pedge> edges =
459int main(
int argc,
char *argv[])
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
helpers for verbose/debug printing
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_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text 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