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