edgepaintmain.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * @dir .
  3. * @brief %edge coloring to disambiguate crossing edges
  4. */
  5. /*************************************************************************
  6. * Copyright (c) 2011 AT&T Intellectual Property
  7. * All rights reserved. This program and the accompanying materials
  8. * are made available under the terms of the Eclipse Public License v1.0
  9. * which accompanies this distribution, and is available at
  10. * https://www.eclipse.org/legal/epl-v10.html
  11. *
  12. *************************************************************************/
  13. #include "config.h"
  14. #include "../tools/openFile.h"
  15. #include <cgraph/cgraph.h>
  16. #include <cgraph/ingraphs.h>
  17. #include <common/pointset.h>
  18. #include <getopt.h>
  19. #include <util/agxbuf.h>
  20. #include <util/alloc.h>
  21. #include <util/exit.h>
  22. #include <util/startswith.h>
  23. #include <util/unreachable.h>
  24. #include <sparse/general.h>
  25. #include <sparse/SparseMatrix.h>
  26. #include <sparse/DotIO.h>
  27. #include <edgepaint/node_distinct_coloring.h>
  28. #include <edgepaint/edge_distinct_coloring.h>
  29. #include <sparse/color_palette.h>
  30. #include <stdbool.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. static char *fname;
  34. static FILE *outfile;
  35. static char **Files;
  36. static void usage (char* cmd, int eval){
  37. fprintf(stderr, "Usage: %s <options> gv file with 2D coordinates.\n", cmd);
  38. fprintf(stderr, "Find a color assignment of the edges, such that edges that cross at small angle have as different as possible.\n");
  39. fprintf(stderr, "Options are: \n");
  40. fprintf(stderr, " --accuracy=e : accuracy with which to find the maximally different coloring for each node with regard to its neighbors. Default 0.01.\n");
  41. fprintf(stderr, " --angle=a : if edge crossing is less than that angle a, then make the edge colors different. Default 15.\n");
  42. fprintf(stderr, " --random_seed=s : random seed to use. s must be an integer. If s is negative, we do -s iterations with different seeds and pick the best.\n");
  43. fprintf(stderr, " --color_scheme=c : palette used. The string c should be \"rgb\", \"gray\", \"lab\" (default); or\n");
  44. fprintf(stderr, " a comma-separated list of RGB colors in hex (e.g., \"#ff0000,#aabbed,#eeffaa\"); or\n");
  45. fprintf(stderr, " a string specifying a Brewer color scheme (e.g., \"accent7\"; see https://graphviz.org/doc/info/colors.html#brewer).\n");
  46. fprintf(stderr, " --lightness=l1,l2 : only applied for LAB color scheme: l1 must be integer >=0, l2 integer <=100, and l1 <=l2. By default we use 0,70\n");
  47. fprintf(stderr, " --share_endpoint : if this option is specified, edges that shares an end point are not considered in conflict if they are close to\n");
  48. fprintf(stderr, " parallel but is on the opposite ends of the shared point (around 180 degree).\n");
  49. fprintf(stderr, " -v : verbose\n");
  50. fprintf(stderr, " -o fname : write output to file fname (stdout)\n");
  51. graphviz_exit(eval);
  52. }
  53. /* checkG:
  54. * Return non-zero if g has loops or multiedges.
  55. * Relies on multiedges occurring consecutively in edge list.
  56. */
  57. static int
  58. checkG (Agraph_t* g)
  59. {
  60. Agedge_t* e;
  61. Agnode_t* n;
  62. Agnode_t* h;
  63. Agnode_t* prevh = NULL;
  64. for (n = agfstnode (g); n; n = agnxtnode (g, n)) {
  65. for (e = agfstout (g, n); e; e = agnxtout (g, e)) {
  66. if ((h = aghead(e)) == n) return 1; // loop
  67. if (h == prevh) return 1; // multiedge
  68. prevh = h;
  69. }
  70. prevh = NULL; // reset
  71. }
  72. return 0;
  73. }
  74. static void init(int argc, char *argv[], double *angle, double *accuracy,
  75. int *check_edges_with_same_endpoint, int *seed,
  76. const char **color_scheme, int *lightness) {
  77. char* cmd = argv[0];
  78. outfile = NULL;
  79. Verbose = 0;
  80. *accuracy = 0.01;
  81. *angle = 15;/* 10 degree by default*/
  82. *check_edges_with_same_endpoint = 0;
  83. *seed = 123;
  84. *color_scheme = "lab";
  85. lightness[0] = 0;
  86. lightness[1] = 70;
  87. while (true) {
  88. // some constants above the range of valid ASCII to use as getopt markers
  89. enum {
  90. OPT_ACCURACY = 128,
  91. OPT_ANGLE = 129,
  92. OPT_COLOR_SCHEME = 130,
  93. OPT_RANDOM_SEED = 131,
  94. OPT_LIGHTNESS = 132,
  95. OPT_SHARE_ENDPOINT = 133,
  96. };
  97. static const struct option opts[] = {
  98. // clang-format off
  99. {"accuracy", required_argument, 0, OPT_ACCURACY},
  100. {"angle", required_argument, 0, OPT_ANGLE},
  101. {"color_scheme", required_argument, 0, OPT_COLOR_SCHEME},
  102. {"random_seed", required_argument, 0, OPT_RANDOM_SEED},
  103. {"lightness", required_argument, 0, OPT_LIGHTNESS},
  104. {"share_endpoint", no_argument, 0, OPT_SHARE_ENDPOINT},
  105. {0, 0, 0, 0},
  106. // clang-format on
  107. };
  108. int option_index = 0;
  109. int c = getopt_long(argc, argv, "a:c:r:l:o:s:v?", opts, &option_index);
  110. if (c == -1) {
  111. break;
  112. }
  113. const char *arg = optarg;
  114. // legacy handling of single-dash-prefixed options
  115. if (c == 'a' && startswith(arg, "ccuracy=")) {
  116. c = OPT_ACCURACY;
  117. arg += strlen("ccuracy=");
  118. } else if (c == 'a' && startswith(arg, "ngle=")) {
  119. c = OPT_ANGLE;
  120. arg += strlen("ngle=");
  121. } else if (c == 'c' && startswith(arg, "olor_scheme=")) {
  122. c = OPT_COLOR_SCHEME;
  123. arg += strlen("olor_scheme=");
  124. } else if (c == 'r' && startswith(arg, "andom_seed=")) {
  125. c = OPT_RANDOM_SEED;
  126. arg += strlen("andom_seed=");
  127. } else if (c == 'l' && startswith(arg, "ightness=")) {
  128. c = OPT_LIGHTNESS;
  129. arg += strlen("ightness=");
  130. } else if (c == 's' && startswith(arg, "hare_endpoint")) {
  131. c = OPT_SHARE_ENDPOINT;
  132. }
  133. switch (c) {
  134. // any valid use of these options should have been handled as legacy above
  135. case 'a':
  136. case 'c':
  137. case 'r':
  138. case 'l':
  139. fprintf(stderr, "option -%c unrecognized.\n", c);
  140. usage(cmd, EXIT_FAILURE);
  141. UNREACHABLE();
  142. case '?':
  143. if (optopt == '\0' || optopt == '?') {
  144. usage(cmd, EXIT_SUCCESS);
  145. }
  146. fprintf(stderr, "option -%c unrecognized.\n", optopt);
  147. usage(cmd, EXIT_FAILURE);
  148. UNREACHABLE();
  149. case 'o':
  150. if (outfile != NULL) {
  151. fclose(outfile);
  152. }
  153. outfile = openFile(cmd, arg, "w");
  154. break;
  155. case 'v':
  156. Verbose = 1;
  157. break;
  158. case OPT_ACCURACY:
  159. if (sscanf(arg, "%lf", accuracy) != 1 || *accuracy <= 0) {
  160. fprintf(stderr, "--accuracy option must be a positive real number.\n");
  161. usage(cmd, EXIT_FAILURE);
  162. }
  163. break;
  164. case OPT_ANGLE:
  165. if (sscanf(arg, "%lf", angle) != 1 || *angle <= 0 || *angle >= 90) {
  166. fprintf(stderr, "--angle option must be a positive real number "
  167. "between 0 and 90.\n");
  168. usage(cmd, EXIT_FAILURE);
  169. }
  170. break;
  171. case OPT_COLOR_SCHEME:
  172. if (!knownColorScheme(arg)) {
  173. fprintf(stderr,
  174. "--color_scheme option must be a known color scheme.\n");
  175. usage(cmd, EXIT_FAILURE);
  176. }
  177. *color_scheme = arg;
  178. break;
  179. case OPT_LIGHTNESS: {
  180. int l1 = 0;
  181. int l2 = 70;
  182. if (sscanf(arg, "%d,%d", &l1, &l2) != 2 || l1 < 0 || l2 > 100 ||
  183. l1 > l2) {
  184. fprintf(stderr, "invalid --lightness=%s option.\n", arg);
  185. usage(cmd, EXIT_FAILURE);
  186. }
  187. lightness[0] = l1;
  188. lightness[1] = l2;
  189. break;
  190. }
  191. case OPT_RANDOM_SEED:
  192. if (sscanf(arg, "%d", seed) != 1) {
  193. fprintf(stderr, "--random_seed option must be an integer.\n");
  194. usage(cmd, EXIT_FAILURE);
  195. }
  196. break;
  197. case OPT_SHARE_ENDPOINT:
  198. *check_edges_with_same_endpoint = 1;
  199. break;
  200. default:
  201. UNREACHABLE();
  202. }
  203. }
  204. if (argc > optind) {
  205. Files = argv + optind;
  206. }
  207. if (outfile == NULL) {
  208. outfile = stdout;
  209. }
  210. }
  211. static int clarify(Agraph_t *g, double angle, double accuracy,
  212. int check_edges_with_same_endpoint, int seed,
  213. const char *color_scheme, int *lightness) {
  214. if (checkG(g)) {
  215. agerrorf("Graph %s contains loops or multiedges\n", agnameof(g));
  216. return 1;
  217. }
  218. initDotIO(g);
  219. g = edge_distinct_coloring(color_scheme, lightness, g, angle, accuracy, check_edges_with_same_endpoint, seed);
  220. if (!g) return 1;
  221. agwrite (g, stdout);
  222. return 0;
  223. }
  224. int main(int argc, char *argv[])
  225. {
  226. double accuracy;
  227. double angle;
  228. int check_edges_with_same_endpoint, seed;
  229. const char *color_scheme = NULL;
  230. int lightness[] = {0, 70};
  231. Agraph_t *g;
  232. Agraph_t *prev = NULL;
  233. ingraph_state ig;
  234. int rv = EXIT_SUCCESS;
  235. init(argc, argv, &angle, &accuracy, &check_edges_with_same_endpoint, &seed, &color_scheme, lightness);
  236. newIngraph(&ig, Files);
  237. while ((g = nextGraph(&ig)) != 0) {
  238. if (prev)
  239. agclose(prev);
  240. prev = g;
  241. fname = fileName(&ig);
  242. if (Verbose)
  243. fprintf(stderr, "Process graph %s in file %s\n", agnameof(g),
  244. fname);
  245. if (clarify(g, angle, accuracy, check_edges_with_same_endpoint, seed,
  246. color_scheme, lightness) != 0) {
  247. rv = EXIT_FAILURE;
  248. }
  249. }
  250. graphviz_exit(rv);
  251. }