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