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